C语言指向数组的双重指针问题:为何q解引用输出值为1100
现象本质:类型不匹配的未定义行为+内存布局巧合
先明确各变量的合法类型边界
- 二维数组
A[2][3]是连续内存的存储结构,本质是“长度为2的一维数组,每个元素是长度为3的int数组”,没有额外的指针成员存储。 int (*p)[3]是数组指针,指向“长度为3的int数组”类型,和二维数组A退化后的首元素指针类型完全匹配,因此p = A是合法赋值。- 对p解引用
*p等价于获取A的首元素A[0](也就是那个长度为3的int数组),数组名会进一步退化为指向首元素的指针,即&A[0][0](也就是1100所在的内存地址)。原代码用%d打印指针是不规范的写法,输出的负数是32位环境下把地址值强转为有符号int的结果。
- 对p解引用
为什么非法赋值后*q会输出1100
首先看q的声明:int (**q)[3],它的合法指向是某个存储了“int(*)[3]类型数组指针”的内存单元的地址。
代码里直接执行q = A属于完全类型不匹配的非法操作,是C标准明确规定的未定义行为(不同编译器、运行平台可能输出完全不同的结果),现在出现的1100只是x86架构32位旧环境下的内存布局巧合:
- 赋值完成后,q自身存储的数值就是二维数组A的首地址(比如记为
0x1000) - 对q做一次解引用
*q时,会按照q的类型约定,从q存储的地址(也就是0x1000)开始,读取一个和数组指针等长(32位下为4字节)的内存内容 - 而
0x1000起始的内存块里,存储的第一个数值就是A[0][0]的值1100 - 最后printf用
%d打印*q时,会把刚才读出来的4字节内容直接当作int型输出,自然就得到了1100的结果 - 这个结果只是巧合:换到64位环境下,因为指针长度为8字节,解引用q时会连续读取8字节内存,也就是把1100和1200拼接成一个长值,打印结果会完全不同,这也能印证这种类型不匹配的赋值属于未定义行为,并非C语言规定的标准逻辑。
q的正确使用方式
如果要让q实现多级指针访问二维数组的逻辑,必须严格按照类型规则赋值:
- 先让数组指针p指向A的首行:
p = A - 再让q指向p的内存地址:
q = &p
此时*q等价于p,**q等价于*p,也就是A[0]的起始地址,行为完全符合预期。
注意:不要将普通双重指针
int **和二维数组的指针逻辑混用。普通双重指针是指向“int类型指针”的指针,内存布局和连续存储的二维数组完全不同,无法直接通过q = A的方式完成类型匹配,必须遵守C语言的类型规则进行赋值。
内容的提问来源于stack exchange,提问作者Varun Saproo
相关产品推荐
相关产品推荐

