博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android NDK开发之旅3 C语言 指针
阅读量:5807 次
发布时间:2019-06-18

本文共 6119 字,大约阅读时间需要 20 分钟。

###前言 学习 C 语言的指针既简单又有趣。通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的。 正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。请看下面的实例,它将输出定义的变量地址:

示例

#include 
int main (){ int var1; char var2[10]; printf("var1 变量的地址: %p\n", &var1 ); printf("var2 变量的地址: %p\n", &var2 ); return 0;}结果输出:var1 变量的地址: 0x7fff5cc109d4var2 变量的地址: 0x7fff5cc109de复制代码

###什么是指针? 指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其它变量或常量一样,您必须在使用指针存储其它变量地址之前,对其进行声明。指针变量声明的一般形式为:

type *var-name;复制代码

在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

int    *ip;    /* 一个整型的指针 */double *dp;    /* 一个 double 型的指针 */float  *fp;    /* 一个浮点型的指针 */char   *ch;     /* 一个字符型的指针 */复制代码

所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

###如何使用指针? 使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:

#include 
#include
void main() { int var = 20; /* 实际变量的声明 */ int *ip; /* 指针变量的声明 */ ip = &var; /* 在指针变量中存储 var 的地址 */ /* 变量 var 的地址 */ printf(" 变量 var 的地址: %p\n", &var); /* 在指针变量中存储的地址 */ printf(" 在指针变量中存储的地址: %p\n", ip); /* 使用指针访问值 */ printf(" 指针访问的变量值: %d\n", *ip); /*对ip存的地址指向的变量进行操作 */ *ip = 66; printf(" 对ip存的地址指向的变量,即对var进行操作后的值: %d\n", var); system("pause");}结果输出:变量 var 的地址: 004FFE3C在指针变量中存储的地址: 004FFE3C指针访问的变量值: 20对ip存的地址指向的变量,即对var进行操作后的值: 66复制代码

###C 中的 NULL 指针 在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。 NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:

#include 
int main (){ int *ptr = NULL; printf("ptr 的值是 %p\n", ptr ); return 0;}结果输出:ptr 的值是 00000000复制代码

在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。 如需检查一个空指针,您可以使用 if 语句,如下所示:

if(ptr)     /* 如果 p 非空,则完成 */if(!ptr)    /* 如果 p 为空,则完成 */复制代码

###利于指针做简单的游戏外挂(DLL注入方式) ####1.游戏程序和外挂程序 先写一个游戏.exe,运行,设定一个游戏时间。

#include 
#include
#include
void main(){ //游戏时间 int time = 600; //打印出time的地址 printf("%#x\n",&time); while(time>0){ time--; printf("游戏时间剩余%d秒\n",time); Sleep(1000);//#include
} system("pause");}复制代码

再创建一个工程(注:生产dll 格式文件),作为外挂,去修改游戏的时间:

#include 
#include
__declspec(dllexport) void go(){ //修改游戏时间 int* p =(int*)0xdcf8a4;//注意:这个地址是游戏打印出来的 *p = 99999;}复制代码

####注意:

  • 添加动态库DLL的输出声明:__declspec(dllexport)。
  • 要保证外挂程序的地址与游戏程序打印出来的地址一致。
  • 把项目的输出改为DLL,而不是EXE。(VS里面:解决方案--属性--常规)。
  • 通过DllInject.exe软件把DLL注入到游戏中,就可以发现游戏时间修改为99999了。

####2.使用DllInject工具注入外挂程序 首先下载

#####(1)点击刷新出现,加载正在运行游戏程序

#####(2)选择目标游戏程序,点击注入按钮,然后注入外挂程序

####3.注入结果

###指针类型 #####指针有类型,地址没有类型 #####地址只是开始的位置,类型大小决定读取到什么位置结束 示例

#include
#include
void main() { int i = 89; int *p = &i; //声明int 类型的指针 double j = 78.9; printf("int size:%d\n", sizeof(int)); printf("double size:%d\n", sizeof(double)); //打印相同类型变量的值 printf("指针p指向地址 %#x, %d\n", p, *p); p = &j; printf("指针p指向地址 %#x, %lf\n", p, *p); //想通过4字节读取8字节变量的值,是不行的 getchar();}结果输出:int size:4double size:8指针p指向地址 0xb7f7dc,89指针p指向地址 0xb7f7c0,0.000000复制代码

只有“int类型的指针”才能用来指向“int类型的值”;其他不同长度类型的指针不行。 指针是指向内存种的一块内存空间,而这块空间的大小要根据指针指向的数据的类型的长度来分配。 比如:int型需要4个字节的空间,double 需要8个字节的空间。 所以在定义指针的时候要指明指针的类型,这样程序才知道应该在内存中保留多大的空间给这个指针

###多级指针(主要讨论二级) ####多级指针的意义:

#####动态内存分配二维数组,操作数组的时候。 #####在jni.h中的struct JNIEnv结构体等有用到。 例子:

#include
#include
//多级指针(二级指针)void main() { int a = -50; //p1上保存的是a的地址 int*p1 = &a; //p2上保存的是p1的地址 int** p2 = &p1; printf("p1:%#x,p2:%#x\n",p1,p2); printf("*p1:%d,**p2:%d\n", *p1, **p2); printf("%d\n", a); **p2 = 90; printf("*p1:%d,**p2:%d\n", *p1, **p2); printf(" a:%d\n", a); getchar();}结果输出:p1:0xddfc50,p2:0xddfc44*p1:-50,**p2:-50-50*p1:90,**p2:90 a:90复制代码

###指针运算(加减法)(与数组的操作相结合)

####指针运算一般在数组的遍历才有意义,基于数组在内存中线性排列方式

#include
#include
void main() { int ids[] = { 78,90,23,65,19 }; //数组的变量名:ids就是数组首地址 3种方式意义一样 printf("%#x\n", ids); printf("%#x\n", &ids); printf("%#x\n", &ids[0]); //指针变量 int *p = ids; printf("%#x\n", p); //p++向前移动sizeof(数据类型)个字节 p++; //指向数组的下一个元素地址 = 数组首地址值+4; printf("%#x\n", p); //指向数组的下一个元素 printf("%d\n", *p); getchar();}结果输出:0x11afc7c0x11afc7c0x11afc7c0x11afc7c0x11afc8090复制代码

####通过使用指针给数组赋值

void main() {	int uids[5];	/*高级写法	int i = 0;	for (; i < 5; i++) {		uids[i] = i;	}*/	//早些版本的写法	int* p = uids;	printf("%#x\n", p);	int i = 0;	//uids + 5 = uids的首地址值+5*4	printf("%#x\n", uids + 5);	for (; p < uids + 5; p++)	{		*p = i;		i++;	}	printf("%d\n", uids[0]);	printf("%d\n", uids[2]);	getchar();}结果输出:0xf3f8880xf3f89c02复制代码

###函数指针(与Java中的回调类似)

####函数指针的定义与基本使用

#include
#include
#include
void msg(char* msg,char* title) { //C语言里面字符串用char指针代替 MessageBox(0, msg, title,0);}void main() { printf("%#x\n", msg); printf("%#x\n", &msg); //函数指针 void(*fun_p)(char* msg, char* title) = msg; fun_p( "消息内容","标题"); getchar();}复制代码

结果输出: 0x41140 0x41140

同时弹出window系统提示框

####在函数指针中调用另一个函数

int add(int a, int b) {	return a + b;}int minus(int a, int b) {	return a - b;}void msg(int(*function_p)(int a, int b), int a, int b) {	//调用函数指针的函数	int res = function_p(a, b);	printf("%d\n", res);}void main() {	msg(add, 1, 2);	msg(minus, 1, 2);	getchar();}结果输出:3-1复制代码

#####案例:用随机数生成一个数组,写一个函数查找最小的值,并返回最小数的地址,在主函数中打印出来

int* getMinPointer(int ids[], int len) {	int i = 0;	int* p = &ids[0];	for (; i < len; i++) {		if (ids[i] < *p) {			p = &ids[i];		}	}	return p;}void main() {	int ids[10];	int i = 0;	//初始化随机数发生器,设置种子,种子不一样,随机数才不一样	//当前时间作为种子 有符号 int -xx - > +xx	srand((unsigned)time(NULL));	for (; i < 10; i++) {		//100范围内		ids[i] = rand() % 100;		printf("%d\n", ids[i]);	}	int* p = getMinPointer(ids, sizeof(ids) / sizeof(int));	printf("%#x,%d\n", p, *p);	getchar();}结果输出:938123535549709265250x10ff8d4,23复制代码

#####注意:

  • 可以看到,msg函数调用的时候需要传一个函数指针,参数是两个整型,返回值是整型。msg函数的执行需要执行我们手动传入的代码(函数),从而实现了回调(注入代码)。
  • 函数指针的使用,Java中new 内部类,类似Java的回调(比回调更加强大)。
  • 函数指针,提高复用性,在C语音的回调机制里面非常重要。

特别感谢:

转载地址:http://zoubx.baihongyu.com/

你可能感兴趣的文章
DevOps 前世今生 | mPaaS 线上直播 CodeHub #1 回顾
查看>>
iOS 解决UITabelView刷新闪动
查看>>
让前端小姐姐愉快地开发表单
查看>>
Dubbo笔记(四)
查看>>
Web前端JQuery入门实战案例
查看>>
java B2B2C Springboot电子商城系统- SSO单点登录之OAuth2.0 登出流程(3)
查看>>
12月26日云栖精选夜读:CDN新品发布:阿里云SCDN安全加速开放公测
查看>>
USB 通信原理
查看>>
7zZip zip RAR iOS
查看>>
date命令的详细用法!
查看>>
分布式存储ceph集群部署
查看>>
UiAutomator源码分析之UiAutomatorBridge框架
查看>>
python 开发之selenium
查看>>
Xcode3.2.5中找不到Mac OS X - Command Line Utility -...
查看>>
css的div垂直居中的方法,百分比div垂直居中
查看>>
如何理解EM算法
查看>>
nginx 域名跳转一例~~~(rewrite、proxy)
查看>>
linux用户家目录无损迁移到独立硬盘
查看>>
文件查找
查看>>
shell编程前言(一)
查看>>