当我在X64计算机上使用GCC编译器运行以下代码时,i
的输出为90,但在x86上运行时其值仍为2,那么处理内存的区别在哪里?
#include <stdio.h>
int main(void)
{
int arr[3]={50,7,30};
int i=2;
arr[3]=90;
printf("arr[2]=%d,arr[3]=%d,i=%d", arr[2], arr[3], i);
return 0;
}
答案 0 :(得分:5)
C中的数组索引从0开始,因此arr
的有效索引是0、1和2。使用arr[3]
会注销数组的末尾。这样做会调用undefined behavior,在您的情况下,它表现为x86和x64上的不同行为。如果您使用其他编译器或更改优化设置,您可能还会得到不同的行为。
关于此特定行为,您似乎认为i
应该立即出现在内存中的arr
之后,以便对arr[3]
的写操作实际上是对i
的写操作。但是,不能保证范围内局部变量的顺序。这不仅仅是x86 / x64的事情。
不要在数组末尾读/写,也不会看到这些类型的问题。
答案 1 :(得分:1)
就像其他人解释的那样,写出数组的边界是UB。
我想指出您可能没有意识到的其他事情: 从您的问题看来,您希望访问 arr [3] 会覆盖您的 i 变量。
即使编译器将按照它们在源代码中出现的顺序分配局部变量(完全不能保证!),您也不会覆盖 i 。
其原因是通常堆栈会向下生长。因此,如果编译器按照堆栈上的分配顺序进行操作,则 i 将获得代码中的最低地址。
但是,为了允许普通的指针算术(使用[]数组表示法时会发生这种情况),增加arr的索引将在堆栈中向上,并且您将覆盖函数的激活记录。太糟糕了。
尝试使用此版本的代码(在GCC中使用-fstack-protector-all进行编译):
#include <stdio.h>
void smash(void){
int arr[3] = {50, 7, 30};
int i = 2;
arr[3] = 90;
//Let's look at the addresses
printf("&i = %p, &arr[0] = %p, , &arr[3] = %p\n", &i, &arr[0], &arr[3]);
printf("arr[2] = %d, arr[3] = %d, i = %d\n", arr[2], arr[3], i);
int i2 = 3;
//Small loop limit can trigger stack protector *upon* return from smash()
//Large loop limit will create a segfault *before* returning from smash()
for(; i2 < 10; i2++)
arr[i2] = 99999;
//Just to see where we crash.
printf("%d\n", i2);
}
int main(void)
{
smash();
return 0;
}
答案 2 :(得分:-1)
在这里,我们看到c的未定义行为,其不同的编译器行为意味着需要执行并执行多少代码,以及从中获取部分代码,这全部取决于编译器。因此,完全是因为编译器会出现此问题。