指针和数组的对比( arr[] 与 &a )
此处引用《C语言深度解剖》中一个表格,很直观的区别了指针与数组
指针 | 数组 | |
---|---|---|
内容 | 保存数据的地址,任何存入指针变量p 的数据都会被当作地址来处理。p 本身的地址由编译器另外存储,存储在哪里,我们并不知道 | 保存数据,数组名a 代表的是数组首元素的首地址而不是数组的首地址。&a 才是整个数组的首地址。a 本身的地址由编译器另外存储,存储在哪里,我们并不知道。 |
访问方式 | 间接访问数据,首先取得指针变量p 的内容,把它作为地址,然后从这个地址提取数据或向这个地址写入数据。指针可以以指针的形式访问 * (p+i);也可以以下标的形式访问p[i]。但其本质都是先取p 的内容然后加上i * sizeof(类型)个byte 作为数据的真正地址 | 直接访问数据,数组名a 是整个数组的名字,数组内每个元素并没有名字。只能通过“具名+匿名”的方式来访问其某个元素,不能把数组当一个整体来进行读写操作。数组可以以指针的形式访问* (a+i);也可以以下标的形式访问a[i]。但其本质都是a 所代表的数组首元素的首地址加上i*sizeof(类型)个byte 作为数据的真正地址 |
应用场景 | 通常用于动态数据结构通 | 通常用于存储固定数目且数据类型相同的元素 |
相关的函数为malloc 和free | 隐式分配和删除 | |
通常指向匿名数据(当然也可指向具名数据) | 自身即为数组名 |
“指针就是指针,数组就是数组,二者并没有任何关系,只是常用相似的外衣来迷惑你”
下面我们来看一下常常出错的地方
1.指针数组
首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称下面这种形式就是的就是一个指针数组:
int *p1[10];
2.数组指针
首先它是一个指针,它指向一个数组。在32 位系统下永远是占4 个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称,形式如下:
int (*p2)[10];
辨别指针数组与数组指针这里需要明白一个符号之间的优先级问题。“[]”的优先级比“ * ”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int * 修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。至于p2 就更好理解了,在这里“()”的优先级比“[]”高,“ * ”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针
3.函数指针
顾名思义,函数指针就是函数的指针。它是一个指针,指向一个函数。如下:
char * (*pf)(char * p1,char * p2);
4.函数指针数组
“char * (* pf)(char * p)”
定义的是一个函数指针 pf。既然 pf 是一个指针,那就可以储存在一个数组里。把上式修改一下:
char * (* pf[3])(char * p);
这是定义一个函数指针数组。它是一个数组,数组名为 pf,数组内存储了3 个指向函数的指针。这些指针指向一些返回值类型为指向字符的指针、参数为一个指向字符的指针的函数
在函数指针数组中,调用函数可以用如下几种方式:
char * (*pf[3])(char * p);
pf[0] = fun1; // 可以直接用函数名
pf[1] = &fun2; // 可以用函数名加上取地址符
pf[2] = &fun3;
5.指向函数指针数组的指针
char * (*(*pf)[3])(char * p);
我们来解析一个语句,加深对指针的理解:
(*(void(*) ())0)();
第一步:void(*) (),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。
第二步:(void(*) ( ))0,这是将0 强制转换为函数指针类型,0 是一个地址,也就是说一个函数存在首地址为0 的一段区域内。
第三步:(* void(*) ( ))0),这是取0 地址开始的一段内存里面的内容,其内容就是保存在首地址为0 的一段区域内的函数。
第四步:(* (void(* ) ( ))0)(),这是函数调用。
char * (*pf[3])(char * p);