Обработка памяти в x64 против x86 - язык C - PullRequest
0 голосов
/ 30 октября 2018

Когда я запускаю приведенный ниже код с использованием компилятора GCC на компьютере с X64, вывод 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;
}

Ответы [ 2 ]

0 голосов
/ 31 октября 2018

Как уже объяснили другие, запись за пределы вашего массива - UB.

Я хотел бы указать на кое-что еще, что вы можете не осознавать: Из вашего вопроса видно, что вы ожидаете, что доступ к arr [3] перезапишет вашу переменную i .

Даже если компилятор выделит ваши локальные переменные в порядке их появления в вашем исходном коде (что вовсе не гарантировано!), Вы НЕ перезапишете i .

Причина этого в том, что обычно стек растет вниз. Поэтому, если компилятор будет следовать вашей последовательности размещений в стеке, i получит самый низкий адрес в вашем коде.

Но чтобы разрешить нормальную арифметику указателей (что происходит при использовании записи массива []), увеличение индексов в arr пойдет на вверх в стеке, и вы перезапишете запись активации вашей функции , Очень плохо.

Попробуйте эту версию своего кода (скомпилируйте с -fstack-protector-all в GCC):

#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;
}
0 голосов
/ 30 октября 2018

Индексы массива в C начинаются с 0, поэтому действительные индексы для arr равны 0, 1 и 2. Использование arr[3] записывает конец массива. Это вызывает неопределенное поведение , которое в вашем случае проявляется как различное поведение на x86 и x64. Вы, вероятно, также получите другое поведение, если будете использовать другой компилятор или измените настройки оптимизации.

Относительно этого конкретного поведения, вы, кажется, думаете, что i должно появиться сразу после arr в памяти, так что запись в arr[3] фактически записывает в i. Однако нет никаких гарантий относительно порядка расположения локальных переменных в области видимости. Это не просто x86 / x64.

Не читайте и не пишите после конца массива, и вы не увидите таких типов проблем.

...