关于C语言数组下标问题

int arr[] = {0, 12, 1, 23,65, 1, 3, 99, 78};
printf("%d\n", arr[-1]);
for (int i = 0; i < 7; i++) {
    printf("%d\n", arr[-1]);
}

结果是

32689
0,1,2,3,4,5,6,

循环中为啥是一串连续的数字呢, 和 i 是如此一样

问题已得到解答, 其实问题可以简化为

int arr[] = {0, 12, 1, 23,65, 1, 3, 99, 78};
int i;
printf("%p\n", arr]);
printf("%p\n", &i);

结果会发现 arr 的地址正好是i后面的一个整型长度的地址(这里有个小问题, 我在wsl上试着是4个字节, 而在mac是两个字节, 但mac上的整型也是4个字节, 不知道为什么这儿就变成两个字节了), 所以 arr[-1] 的地址正好与i的地址一致

阅读 2.8k
4 个回答

越界了,undefined behavior。
你这个代码中,都是在栈上分配的,栈是高位往低位扩张,一开始栈的布局是这样的
a9
a8
a7
a6
a5
a4
a3
a2
a1
a0
每次loop,会在低位申请然后又释放i,栈的布局:
a9
a8
a7
a6
a5
a4
a3
a2
a1
a0
i
这种情况下arr[-1]就会一直输出i

这里有个小问题, 我在wsl上试着是4个字节, 而在mac是两个字节, 但mac上的整型也是4个字节, 不知道为什么这儿就变成两个字节了)

我猜你计算2和4是代码里面输出两个地址相减的值的。
你的wsl应该是默认编译出32位的程序,直接减得到4没问题。
mac上编译出来的是64位的,你直接减,然后输出的时候应该是溢出了,得到个2,你试试先把地址转long long,用%lld输出试试,会得到一个8,而不是2或者4。
这是因为分配空间的时候还会做内存对齐,分配i的时候会再跳过高位的4个字节再去分配i,保证i的地址也是8的倍数,所以实际地址差是8,64位的程序堆结构大概如下:
0 a8
a7 a6
a5 a4
a3 a2
a1 a0
0 i

贴一个gdb的输出

// code
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int i = 2;

// gdb
p &i
(int *) $1 = 0x00007ffeefbffdc8
x/11dw 0x00007ffeefbffdc8
0x7ffeefbffdc8: 2
0x7ffeefbffdcc: 0
0x7ffeefbffdd0: 1
0x7ffeefbffdd4: 2
0x7ffeefbffdd8: 3
0x7ffeefbffddc: 4
0x7ffeefbffde0: 5
0x7ffeefbffde4: 6
0x7ffeefbffde8: 7
0x7ffeefbffdec: 8
0x7ffeefbffdf0: 9

下标越界。

向后越界(比 0 还小)指向低位指针地址,向前越界(比数组长度还大)指向高位指针地址。如果指向的地址没被使用,也就是个野地址,那么结果就是是随机的,无意义。

这个代码段里第一个 arr[-1] 指向什么还得看上下文,不同的上下文结果不一定都是 32689;for 循环里的 arr[-1] 指向 i 了。

这种黑魔法少用,基本没有可读性。

int arr[] = {0, 12, 1, 23,65, 1, 3, 99, 78};
printf("%d\n", arr[-1]);

对于,上述的 arr[-1];,值是未定义的,array[-1] == *(arr - 1),我们不知道 arr 前面的值是什么,整数,浮点数,指针,结构体,共同体?

for (int i = 0; i < 7; i++) {
    printf("%d\n", arr[-1]);
}

对于上述程序,我觉得,你可能是想输出数组中的每一个元素,正确的做法应该是:

for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i) {
    printf("%d\n", arr[i]);
}

下表越界是一个未定义行为

未定义行为什么都可能发生

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题