四、指针高级应用总结
1 笔试积累
-
指针数组本质是数组,数组元素全为指针。
-
数组指针本质是指针,指向的是一个数组。
-
typedef
的意义在于简化书写
和创造平台无关性
。 -
typedef
重定义的类型不会原地展开,也就是说经其被重定义的类型享有编译器与原生类型同等的待遇。而#define
宏定义会原地展开。 -
使用
typedef
可以同时定义出结构体变量和该结构体指针。 -
二重指针本质仍然是指针变量,只不过其指向的变量类型是一重指针(普通指针)。
-
数组初始化时,可以在大括号内直接使用下标指定初始化特定元素,例如
int a[10] = {[3]=3};
,其存在的意义为可以省略依次书写时,对前面元素的初始化操作。
2 理解和思想
2.1 先后结合看本质
- 在复杂表达式中,若没有小括号的处理,则必须依据优先级进行结合处理。
- 由于括号优先级较高的原则,可以判定
int *p[10];
中,p
首先和括号结合,也就是说符号p
的本质是一个数组,由于其类型为int *
,可知该数组中每个元素均为指向int
类型的数据的指针。 - 同理可判断
int (*p)[10];
本质是一个指针,其指向的是一个具有5
个元素的数组,每个元素的数据类型为int
。 - 符号本质判定:先与
*
结合为指针
,先与[]
结合为数组
,先与()
结合为函数
。
2.2 函数指针的本质
- 函数指针本质也是指针变量,只不过其指向的数据是一段代码的起始地址。
- 指针变量指向的数据内容依据其定义的类型,例如普通整型指针
int *p;
其指向类型为int *
,数组指针int (*p)[10];
其指向类型为int (*)[10]
,而函数指针int (*pfun)(int);
其指向类型为int (*)(int);
即指向一个返回值为int
,有一个输入参数且为int
类型的这样的一个函数。 - 函数名称前取地址
&pfun
其意义及数值均和函数名pfun
一致,这一点需要区别于数组名。
2.3 typedef与指针
typedef
经常被用来简化函数指针的书写样式,然后便于将函数指针封装在结构体内部,间接完成面向对象编程
的功能,其目的也是为了代码结构清晰,简化复杂程序的代码量
和统一抽象代码
逻辑。- 使用
typedef
定义的类型可以连续定义多个指针变量:typedef int * user_t ; user_t a,b,c;
,但#define user_t int*
则定义多个变量后原地替换:int * a,b,c
,相当于int *a;int b;int c;
。也就是说指针类型(如int *
)并不会被编译器接受为一个原生类型,除非用typedef
重定义。
2.4 const限定失效
- 经过
typedef
重定义后指针类型加const
限定会出现看起来失效的现象。 typedef int * user_t ;
则const user p
与user const p
均等同于int * const p2
。也就是说重定义int *
未被限定,则使用该重定义时将无法更改他的限定关系,也就是说前面两种情况const
都只能修饰指针p
,而无法修饰其指向的数据。如果确实需要限定其指向的内存数据,那么就必须要在重定义时进行限定,一旦重定义完成,该原型将被保护。
2.5 双重指针本质
- 都是指针变量,表现为其长度均为4个字节(32位系统)。
- 指针如何指向另一个变量?指针变量本身存储的是另一个变量的地址。
- 二重指针存储的是什么?存储一重指针变量的地址。即二重指针指向的是一个普通指针变量。
- 编译器类型匹配检查中,举例二重指针
int **p
指向int *
类型,那么指针数组int *q[3]
的数组名q
与二重指针p
类型匹配,分析:数组名是该数组首元素首地址,该数组首元素是一个指针q[0]
,对该指针变量取地址即为数组名q
,也就是说其数组名就是一个二重指针。
2.6 数组名和指针类型匹配
int a[10]
一维数组名即为首元素首地址&a[0]
,也就是可以被一个普通的一维指针所指向的地址。即int *p = a;
是类型匹配的,由此可以推断,二维数组中类型匹配时,将此处的p
指向一个普通int
类型变量替换为指向一个一维数组且数组元素为int
类型即可。int b[5][10]
二维数组名即为首元素首地址&b[0]
,只不过需要注意的是,这里的首元素是仍然是一个一维数组,也就是说b[0]
仍然可以作为一个数组名,即等价为&b[0][0]
,所以综上可得b
等价为&&b[0][0]
。因为需要使用一个合适的类型与数组名匹配(两个取址符),此时的数组名表示的是一个数组**(元素个数为10的int 类型
数据)的首地址,而能指向一个数组的首地址的指针变量可以是数组指针(指向元素个数为10的int 类型
数据)**,即int (*p)[10] = b;
是类型匹配的。
2.7 数组的指针访问
-
首先需要明确数组下标访问其本质是编译器在使用指针访问。
-
一维数组和二位数组在访问效率上无任何差异,其目的只有一个,方便程序员编写代码。
-
掌握指针访问数组元素的关键是:指针步进值,也就是指针在与数字运算时的意义。
-
一维数组及指针
int a[10] ; int *p = a ;
,访问元素a[3]
即相对于首元素移动了3
个步进值即可,而由指针p
定义可知,该步进值为sizeof(int)=4
。直接使用*(p+3)
即可访问a[3]
。 -
二位数组使用指针方式访问,需要两次解引用,第一次解引用得到在一维数组中的指针位置,之后步进值由原来的一维步进值(即指向的数组的长度)变为二维步进值(指向变量类型的大小)。
int b[5] [10];
int ( *p )[10] = b;
则* (p+1)
取得a[1]
地址,实际上+1
操作是对指向的数组进行操作,步进值为10*4
,也就是说指针实际移动了1*40=40个单位
。经过一次解引用过后步进值变成了指向变量的步进值4
,接着*(*(p+1)+2)
即从a[1][0]
移动到了a[1][2]
,实际上+2
操作指针移动了2*4=8个单位
。 -
二维数组使用指针访问时也可以采用一维数组指针访问的方法,只不过需要自己计算,例如
int b[5] [10];
int *p = &b[0][0];
,访问a[1][2]
,需要计算偏移量,且只能一次解引用(因为是一个一维指针):*(p+1X10+2)
,即*(p+12)
。