一、指针简介
指针是C语言的灵魂,C语言之所以强大,很大一部分原因在于对指针的灵活运用。我们无论需要对内存的精准分配和释放,还是对接口api的使用,乃至面向对象中的类和对象的封装,都涉及到了指针。C语言的指针大致可以分为两种,一种是作为一个变量,其保存的是一段内存地址,也就是本文要谈的多级指针;另一种是作为一种数据类型,像函数指针,用于引出一种类型,主要用于回调函数。
二、指针与基础数据类型
从内存的角度看,数据类型就是一段固定大小内存块的别名。
int a=0;
int在32/64位操作系统中都是占用4个字节,这段简单的代码相当于告诉编译器,让操作系统在栈区分配一块占4个字节的内存块,命名为a,并且初始化为0,a为变量名。
因此我们可以将这块内存的地址赋值给一个指针变量。
int* p=&a;
即p保存了a的地址,通过*p可以间接的改变a的值。而这样修改最大的好处是在于在函数调用中,或者说在不同模块中能够修改a的值。下面讲讲在开发中遇到的情形
char* p=NULL; char buf[32]=" key1= value";
假设上述代码是在主调函数中定义的,那么指针p在向NULL,意味着我们希望能在被调用分配内存,然后使p指向所分配的内存空间,那么此时就需要使用二级指针来存放p的地址了,假设我们需要设计一个去除字符串中的空格的函数,那么其接口如下
int ReSpace(char* p1,int len,char** p2,int* len2) {.....}
调用方法:
char buf[32] = "key1= value"; char* p = NULL; int len1 = strlen(buf); int len2 = 0; ReSpace(buf,len1,&p,&len2)
即函数将去除空格的字符串通过p传出来了。同时二级指针也可以记录字符数组,或者二位数组的内存地址。因此也可以推出三级指针甚至是多级指针的用法了,即用来修改二级指针的值。
三、指针与结构数据类型
1、结构体类型:
有以下一段代码
typedef struct _SCK_HANDLE { char version[64]; char ip[128]; int port; unsigned char *p; int plen; }SCK_HANDLE; void main() { SCK_HANDLE *hdl = NULL; hdl = (SCK_HANDLE *)malloc(sizeof(SCK_HANDLE)); }
即声明了一个结构体类型的指针,并且给它分配了内存空间,那么就可以将hdl指针作为一个参数传递给其他模块来改变这块内存中的值。
2、数组指针
初学C语言往往很难理解数组指针,也很容易和指针数组相混淆,事实上,如果只要明白数组指针就是一个行指针,指向的是一个数组,保存的是一个数组的地址。它的步长是数组的列数
int c[5]; int (*pointer)[5] = &c; int buf[10][30]; int (*p)[30]=buf; // 指向二维数组的首行
四、使用指针时常见的错误
1、用指针修改内存中的值时,应该确保所指的值能够被修改。如:
char* p="abcdefg"; p[0]=‘w‘; //错误,指向的是常量区的字符常量
2、注意深拷贝和浅拷贝,即应该明确用指针分配了多少内存空间,以免出现重复释放内存的问题。
3、指针的步长问题
指针步长和其存放的数据类型有关,每移动一步,编译器都会乘以其存放数据类型的长度,相当于移动到下一个数据。
4、sizeof 指针 求的是指针变量所占的内存,sizeof 数组名 求的 是数组所占的内存。
5、避免出现“野指针”,即释放指针所指的内存空间后,将指针置NULL。
总结
指针是C语言中很细致的一个语法,再怎么说也说不完指针的所有应该场景。但是我们只要抓住它的根本,即从内存的角度去理解,分析,那么它就变得很好理解。我们在平时的学习中,多去应用它,学习指针的思想,站在内存的角度去思考,那么整个C语言的学习都会有很大的提升。