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

时间:2018-10-30 18:49:08

标签: c memory x86 64-bit x86-64

当我在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;
}

3 个答案:

答案 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的未定义行为,其不同的编译器行为意味着需要执行并执行多少代码,以及从中获取部分代码,这全部取决于编译器。因此,完全是因为编译器会出现此问题。