C语言中数组下标与指针算术运算结果不同的原因探究
为什么数组下标语法取地址得到无效地址,而指针算术却正常?
在C语言中,使用数组下标语法获取数组元素地址时得到了无效地址,但用指针算术运算却能得到预期的正确地址,核心原因在于无符号整数的溢出特性和数组下标表达式的运算规则,结合代码示例分析如下:
问题复现代码(变量arg版本)
编译环境:gcc C99/C11标准,编译选项-Wall -O0
#include <stdio.h> int main() { unsigned int arg = 0; char arr[5] = {'a', 'b', 'c', 'd', 'e'}; char* arrp = arr + 2; char* pos_1 = arrp - arg - 1; char* pos_2 = &arrp[-arg - 1]; printf("arr: %p\narrp: %p\npos_1:%p\npos_2:%p\n", arr, arrp, pos_1, pos_2); return 0; }
运行结果
arr: 0x7fff31035a73 arrp: 0x7fff31035a75 pos_1:0x7fff31035a74 pos_2:0x800031035a74
可以看到pos_2的地址明显无效(高位多了0x8000)。
常量替代后的正常代码
当用常量0代替变量arg时,pos_2能得到正确地址:
#include <stdio.h> int main() { char arr[5] = {'a', 'b', 'c', 'd', 'e'}; char* arrp = arr + 2; char* pos_1 = arrp - 0 - 1; char* pos_2 = &arrp[-0 - 1]; printf("arr: %p\narrp: %p\npos_1:%p\npos_2:%p\n", arr, arrp, pos_1, pos_2); return 0; }
运行结果
arr: 0x7ffc270c4ed3 arrp: 0x7ffc270c4ed5 pos_1:0x7ffc270c4ed4 pos_2:0x7ffc270c4ed4
原因分析
无符号整数的运算特性:
arg是unsigned int类型,C语言中无符号整数的运算遵循模2^n规则,不存在负数。当计算-arg -1时,-arg会被转换为对应无符号类型的最大值(比如32位unsigned int是0xFFFFFFFF),再减1得到0xFFFFFFFE——这是一个极大的正数,而非预期的-1。数组下标表达式的等价规则:
C标准规定,arrp[N]等价于*(arrp + N),因此&arrp[N]本质就是arrp + N。在&arrp[-arg -1]中,N即-arg -1,由于arg是无符号数,整个表达式的结果是无符号类型的极大值,将这个值作为偏移量加到arrp上,直接导致指针地址溢出,得到无效的高位地址。指针算术运算的运算顺序与类型转换:
对于arrp - arg -1,运算顺序是(arrp - arg) - 1:- 第一步
arrp - arg:arrp是char*类型,arg是unsigned int(值为0),会触发寻常算术转换,指针地址被转换为对应宽度的无符号整数类型,减去0后仍等于arrp的地址; - 第二步减1:这里的
1是int类型,指针减去1等价于向前偏移一个char的长度,结果正确。
- 第一步
常量编译期优化的影响:
当arg是常量0时,编译器在编译期就能直接计算出-0 -1 = -1,这个值是有符号整数,此时arrp + (-1)等价于arrp -1,自然得到正确地址。
内容的提问来源于stack exchange,提问作者Newb
相关产品推荐
相关产品推荐

