标签:
上一篇笔者用那不是怎么好理解的逻辑介绍了内存和C中的基本数据类型,现在笔者再根据自己重新所学来说说C语言中的指针。
理解指针才能真正的算C语言入门。也许是我大学期间太关注前端UE,也许是当初开始学C语言的时候没怎么认真;直到毕业后的某一天我才“懂”指针,才算理解C语言的伟大。如果早点理解它,可能我毕业时就会选择一份不一样的工作。如果有初学C语言的同行对指针有困惑,希望我这浅薄的认识能帮助你。
1、简介
指针在原英文中为pointer,个人觉得翻译过来后针的含义不如指的含义好理解,pointer还可翻译为指示器,如果是初学者的话,笔者建议在学习过程中多琢磨指的含义。
指针可以说是一种特殊的数据类型,前面的内存篇介绍过,程序运行时的数据基本上都存储在内存中,内存中用数字标识不同数据,在各种计算机语言中,把这种数字标识成为地址。计算机系统并不认识C语言,程序编译就是把C语言翻译为操作系统可识别的语言。比如“我”翻译英文为I,翻译过后已经看不到“我”这个字了。我们在程序中定义了整型变量i,赋值为5,那么经过编译运行,操作系统同样看不到i,对操作系统而言,i被翻译为某个内存地址上的整型值。
2、基本使用
要使用指针首先要认识两个符号:一个是‘&‘,可以在程序中取得变量的内存地址;一个是‘*‘,‘*‘在定义变量的时候标明该变量为指针,在已定义的变量前面使用的时候,表示获取(设置)该指针变量所在内存地址中存储的值。
1 #include <stdio.h> 2 3 int main(int arg, char * args[]) 4 { 5 int i = 5; // 定义整型变量i并赋值为5 6 int * srcI = &i; // &为取地址符号,取得i的内存地址,并赋值给int *的整型指针类型变量srcI 7 printf("srcI:%d,%X\n", srcI, srcI); // 内存地址可以转化为数据打印出来,一般使用16进制查看 8 printf("*srcI:%d\n", *srcI); // 在定义变量的时候,符号*表示定义指针, 9 // 在对已定义的变量的前面加上*的时候,符号*表示取得该变量所标地址中变量的值 10 *srcI = 7; 11 printf("i:%d\n", i); // 可以通过地址设置变量的值 12 getchar(); // 起暂停作用 13 return 0; 14 }
上面代码执行结果如下(每次程序运行时变量的内存地址可能会不同):
1 srcI:7665592,74F7B8 2 *srcI:5 3 i:7
笔者认为理解指针重点就在理解‘*‘这个符号,这里强调下,在定义变量时,‘*‘表示后面定义的变量是指针类型,在已定义的指针前‘*‘表示获取(设置)后面变量存储的内存地址中实际存储的值,专业点说就是获取(设置)后面变量所指向的值。下面列举一个未理解‘*‘含义时会犯的错误:
1 #include <stdio.h> 2 3 int main(int arg, char * args[]) 4 { 5 int i = 5, j = 6; 6 int * src = &i; 7 *src = &j; // 本意是把src的地址由i的地址改为j的地址,但这里弄错了*的含义 8 printf("*src:%d\n", *src); 9 printf("i:%d\n", i); // 实际结果改变了变量i的值 10 getchar(); 11 return 0; 12 }
上述代码编译运行都没有报错,本意是想把src存储的地址改为变量j的内存地址,而实际却变成设置变量i的值,这就是对‘*‘错误的理解后果。这段代码还说明了一件事情:第7行地址可以赋值给整型变量,这也证实了我前面的话:内存中用数字标识不同数据,该数据标识就是内存地址。
3、指针高级
之前只使用了整型作为示例,而实际上‘&‘可以用来取得所有数据类型的地址,如char,float,double等等;用‘*‘定义指针的时候,同样可以定义出 char *,float *,double *这些指针类型,不同的指针类型在读取数据的时候的不同点就在于其解释地址的方式不同。如int *类型,定义后使用*取值,会从int *所代表的地址开始,取sizeof(int)位地址的数据,然后以int的方式解析出数据,使用其他数据类型以此类推。
指针类型可以存储所有数据类型的地址,那么指针是否可以存储指针的地址呢?确实可以,有关多级指针的问题,这里举个可能不是很恰当的例子:在班级课堂上你需要一支笔,于是你向你同桌要,同桌说ta后面的人有笔,然后你再向同桌后面的人要,同桌后面的人说ta旁边的人有,于是你再向同桌后面旁边的人要......如果需要的话,这个场景可以循环下去。多级指针大概就是这么个意思,你读取这个地址发现里面还是个地址,于是再读里面的地址......当然现实中遇到这种借笔借半天的问题肯定很坑,但是对于计算机指针来说,只是多一个符号的问题。下面做个简单示例:
1 #include <stdio.h> 2 3 int main(int arg, char * args[]) 4 { 5 int i = 6; 6 int * src = &i; 7 int ** srcsrc = &src; // 多级指针无非就是多个*,多次& 8 int *** srcsrcsrc = &srcsrc; 9 10 printf("*src:%d\n", *src); 11 printf("**srcsrc:%d\n", **srcsrc); 12 printf("***srcsrcsrc:%d\n", ***srcsrcsrc); 13 14 // 同样可以设置值 15 *src = 8; 16 printf("i:%d\n", i); 17 **srcsrc = 10; 18 printf("i:%d\n", i); 19 ***srcsrcsrc = 12; 20 printf("i:%d\n", i); 21 getchar(); 22 return 0; 23 }
执行结果如下:
*src:6 **srcsrc:6 ***srcsrcsrc:6 i:8 i:10 i:12
4、指针运用
说了这么多,指针在实际使用中有何作用?首先,你发现指针读取的都是操作系统内存地址,所有程序的数据都存在系统内存中,如果能读取设置他们所有的值,那么也就可以通过内存地址修改系统其他程序的数据,这就是修改器、外挂的部分原理,实际想这么做还要考虑如何找地址,如何通过系统的内存保护(注入)。
首先介绍下指针与数组。定义一个容量为6的整型数组int array[6],其实此时array就是一个指针,其地址就是该数组中第一个元素的地址。关于数组和指针可以看下面代码及其注释:
1 #include <stdio.h> 2 3 int changeArr(int * arr) 4 { 5 arr[0] = 20; // 正常方式赋值数组元素 6 *(arr + 1) = 40; // 指针方式赋值数组元素 7 } 8 9 int main(int arg, char *args) 10 { 11 int i = 0; 12 int array[6] = { 1, 2, 3, 4, 5, 6 }; 13 printf("*array:%d\n", *array); // 可以直接通过*取得首元素值 14 15 // 正常方式遍历数组 16 printf("array:"); 17 for (i = 0; i < sizeof(array) / sizeof(int); i++) 18 { 19 printf("%3d ", array[i]); 20 } 21 printf("\n"); 22 // 指针方式遍历数组 23 printf("array:"); 24 for (i = 0; i < sizeof(array) / sizeof(int); i++) 25 { 26 printf("%3d ", *(array + i)); 27 } 28 printf("\n"); 29 30 // 通过带指针参数的函数对数组值进行修改 31 changeArr(array); 32 // 指针方式遍历数组 33 printf("after change\narray:"); 34 for (i = 0; i < sizeof(array) / sizeof(int); i++) 35 { 36 printf("%3d ", *(array + i)); 37 } 38 printf("\n"); 39 getchar(); 40 return 0; 41 }
执行结果如下:
1 *array:1 2 array: 1 2 3 4 5 6 3 array: 1 2 3 4 5 6 4 after change 5 array: 20 40 3 4 5 6
二级指针应用和三级指针应用一般在开发动态库的时候才会用得到,有兴趣的可以自行查查相关资料,本文中暂不赘述。
5、函数指针引子
一般来说,一个人能运用函数指针说明其水平正从入门走向熟练,笔者自己用的基本都是在一些系统调用下才会用到,一般用于回调,这里就做个引子,简单说说其定义及使用。请看下图中代码及注释:
1 #include <stdio.h> 2 3 void func() 4 { 5 printf("hello func pointer\n"); 6 } 7 8 float addFloat(float a, float b) 9 { 10 return a + b; 11 } 12 13 14 int main(int arg, char *args[]) 15 { 16 void (*f)() = func; // 定义函数指针并初始化 17 float(*addf)(float a, float b) = addFloat; // 带参数的函数指针 18 f(); // 可通过该函数指针直接执行 19 printf("%.1f+%.1f=%.1f\n", 3.0f, 4.0f, addf(3.0f, 4.0f)); 20 getchar(); 21 return 0; 22 }
执行结果如下:
1 hello func pointer 2 3.0+4.0=7.0
我对于指针的部分理解及运行暂时就先到这里,如果有问题还请大家指出,谢谢!
标签:
原文地址:http://www.cnblogs.com/yaoh/p/4343201.html