相信学习过C语言的童鞋,一定被这2个东西折腾过吧?究竟它们都是何方神圣呢?带着这个问题,笔者想通过本文给你一个清晰的答案。通过阅读本文,你将了解到以下内容:
【数组指针】,从字面意思上理解,就是一个【指针】;“数组”只是起到了修饰“指针”的作用,所以连起来的意思就是【指向数组的指针】。这一点与上一篇文章介绍 【函数指针】有异曲同工的含义。
从C语言的语法上理解,数组指针的表示形式为:
//定义一个一维数组
int a[3];
//定义一个指针,指向一维数组的首地址
int *q = a;
//定义一个3行4列的二维数组
int b[3][4];
//定义一个数组指针,它指向二维数组的首地址
int (*p)[4] = b;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQIadh0d-1661923181862)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
从数组指针的形式上看,因为()运算符拥有最高优先级,所以整个语句优先被解释成一个指针;接着,这个指针再指向另一个数组的首地址,[x]接上该数组的列数,即得到如上的数组指针的定义。
经过这个例子,我们可以看到,数组指针一般用于表达多维数组,对比起多维数组的表示,采用数组指针的形式可以在一定程度上理解难度减小了。比如,有了如上的数组指针定义后,这里b是个二维数组的数组名,相当于一个二级指针常量;p是一个指针变量,它指向包含5个int元素的一维数组,此时p的增量以它所指向的**一维数组长度为单位;*(**p+i)是一维数组b[i][0]的地址;(p+2)+3表示b[2][3]地址(第一行为0行,第一列为0列),(*(p+2)+3)表示b[2][3]的值。
【指针数组】,从字面意思上理解,它就是一个数组,只不过“指针”是用于修饰“数组”的,所以合起来理解就是:【一个数组元素存放的是指针的数组】。
从C语言的语法上理解,【指针数组】的定义形式如下所示:
//定义一个char *的指针数组
char *p[5];
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qud5CpbF-1661923181865)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
这里,它表示的含义是,一个由5个元素组成的一维数组,每个数组元素都是一个指针(地址)。访问数组的元素,我们都是采用 数组名[数组下标] 的形式访问的,那么【指针数组】也不例外,访问第一个元素,则是p[0];同理,p[2]表示的是第3个指针。
从字面上看,确实很容易混淆两者的概念;我们理解的时候,需要注意名词谁先谁后。一般来说,在前面的名词是用于修饰后面的名词,而后面的名词决定了整个词组的性质。
【数组指针】:数组修饰指针,它的本质是一个指针;一般这个指针指向一个二维数组,形式为: int (*p)[M]。
【指针数组】:指针修饰数组,它的本质是一个数组;这个数组里面的元素,存放的都是指针,形式为: int *p[M]。
如上定义中,第一个M表示二维数组的列数,第二个M表示的指针数组(一维数组)的元素个数。
数据访问方面:
这两个概念不但容易混淆,而且在使用过程中也是十分容易出错,曾经笔者在【指针数组】上摘过跟头。现将出错的教训分享给大家:
比如有一个指针数组的定义:
char *p[5];
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CRsvrkY7-1661923181868)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
假设在编程平台上,一个指针所占用的地址空间是4个字节,即sizeof(char ) = 4;那么如果使用sizeof§去求这个指针数组所占用的地址空间时,求得的大小是45=20;而每个元素都是char *类型,所以求得指针元素的个数为: 20 / 4 = 5。
于是,我们得出一个公式,求指针数组的元素个数:
//直接求得指针数组p的元素个数
cnt = sizeof(p) / sizeof(p[0]);
//很多时候,我们会定义一个宏来表示,形式如下:
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif
//使用ARRAY_SIZE宏求指针数组p的元素个数
cnt = ARRAY_SIZE(p);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qwfXvoOo-1661923181869)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
假设有以下2个指针数组的定义:
const char *p1[] =
{
"12345",
"23456",
"34567",
"45678",
"56789",
};
const char *p2[] =
{
"12345",
"23456"
"34567",
"45678"
"56789",
};
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wt3RK9bs-1661923181874)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
如果你不仔细看,你可能觉得p1和p2的定义是一致;仔细一看,原来p2中少了2个分号;而这2个分号一少,直接就导致p2的最终被编译器识别成的定义为:
//最终被识别的定义
const char *p2[] =
{
"12345",
"2345634567",
"4567856789",
};
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aNeQ6PqX-1661923181875)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
看到区别了吗?由于分号的缺失导致前后相邻的字符串被结合在一块,被连接成一个更长的字符串,而这种【拼接】是编译器自动识别完成的,它不会提示任何错误,因为在它看来根本就不是错误。对使用者而言,这样定义一改变,原本本应该为5个元素的字符串数组,就变成了3个字符串的数组,这简直就是灾难啊!!!
以上就是笔者对【数组指针】和【指针数组】的实践,得出的切实理解,希望能够帮助大家更近一步地理解它们。以上提及观点,均为笔者本人的观点,如有纰漏之处,还望指正。感激不尽。
全部0条评论
快来发表一下你的评论吧 !