欢迎光临散文网 会员登陆 & 注册

C/C++指针陷阱——对数组名取址

2023-01-08 15:07 作者:28283844972_bili  | 我要投稿

剧透:数组名不是指针变量,仅在特定的用法上等同于指针;数组名是地址常量,因此无法更改自身所表示的地址(但并非指针常量);对数组名取址在C语言标准中属于未定义行为,具体取决于编译器的实现,通常对数组名取址的表现与数组指针等同(仔细想想如何给一个数组指针赋值)。

来看一小段代码片段引出本文的话题,作用是读取文件内容,在向read库函数传入第二项void *__buf参数时意外使用了数组名取址,正确的传参形式同注释部分,但最终都能正常读取文件内容:

如此看来,数组名与对数组名取址的结果似乎可以画上等号?就像函数名与函数名取址那般。如果说数组对应的是数组的首地址,那么对数组名取址自然是对首地址取址,结果至少也得是个二级地址(初学者的正常想法),如果把它作为缓冲内存地址传参那不得乱套?但就是莫名其妙地能跑🤣。

那就先从read库函数的声明入手,描述如下:

基本可以排除语法问题,void *类型说它收到的确实是指针类型,是个指针都行,没毛病;C/C++编译器说事到如今都听你的,用户永远都是对的;我。。。

快速分析一下问题的切入点一定是数组名和数组名取址所表达的含义,以及经历相同运算后的结果含义,测试代码如下:

测试结果如下:

从结果中可以看到数组名(array)、对数组名取址后的内容(&array)均可以表示数组首个元素的地址,即数组首地址,这也就可以解释向read库函数中传入取数组名地址后的结果作为参数,程序依旧能如期完成任务,最终的void *类型指针总能找到正确的缓冲区地址。

别急,数组名和对数组名取地址结果并非在任何场景下都能画上等号,相反他们俩是完全不同的概念。取两者的下一处地址进行比较,同时加入一个等价的数组指针作比较,可以发现数组名的计算步长与数组元素大小一致,数组名取址的运算步长与数组整体大小一致,与等价的数组指针的步长一致。因此对数组名取址的结果等价于数组指针。另外从元素的类型大小可以得出,数组名表现地更像是一种数据结构类型,数组名取址则更加接近于普通的指针。

尽管C语言标准未给出对数组名取址的具体行为定义,但现代编译器普遍认为对数组取址的结果是数组指针的右值,从以下这条语句就可以看出。只不过在最初案例中传参的时候因为某种疏忽或理解偏差而错误使用,但最终还是阴差阳错地跑起来了。虽然结果正确,但软件中的错误就是这么日积月累起来的。

明明是很简单的概念理解偏差问题,但杂糅在一起就被唬住了,一时半会儿也转不过弯,往往在很久之后的某一瞬间才猛然醒悟,时间和精力就是这么从我们手上悄悄溜走的。



C/C++指针陷阱——对数组名取址的评论 (共 条)

分享到微博请遵守国家法律