x64 与 x86 中的内存处理 - C 语言

Memory handling in x64 vs x86 - C language

当我 运行 下面的代码在 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;
}

C 中的数组索引从 0 开始,因此 arr 的有效索引是 0、1 和 2。使用 arr[3] 是注销数组的末尾。这样做会调用 undefined behavior,在您的情况下,它在 x86 和 x64 上表现为不同的行为。如果使用不同的编译器或更改优化设置,您可能还会得到不同的行为。

关于这个特定的行为,您似乎认为 i 应该在内存中紧跟在 arr 之后出现,这样写入 arr[3] 实际上就是写入 i。但是,对于范围内局部变量的顺序并不能保证。这不仅仅是 x86 / x64 的事情。

不要读取/写入超过数组末尾的内容,这样您就不会遇到这些类型的问题。

就像其他人解释的那样,写出数组的边界是 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;
}