Компилятор G возвращает один и тот же адрес переменной после каждого запуска (даже после изменения имени)

#c #visual-c #g #compiler-optimization #memory-address

#c #visual-c #g #компилятор-оптимизация #память-адрес

Вопрос:

Я предполагаю, что компилятор изменяет адрес переменной после каждого запуска. (И это происходит в MSVC)

По какой-то причине компилятор G для меня возвращает один и тот же адрес при каждом запуске.

  1. Даже после закрытия и повторного запуска он возвращает прокомментированный адрес. Существует ли какой-либо метод оптимизации, о котором я не знаю?
  2. После изменения имен переменных также возвращается тот же адрес.

Хотите понять разницу между MSVC и G и причину этого.

 #include<iostream>
using namespace std;

int main(){
   int b[] = {23,4,6,1,5,7,8,7};
   cout << b; //0x61fef0
   cout << endl;
   cout << amp;b[0]; //0x61fef0

   return 0;
 

}

Примечание: я использую g (MinGW.org Сборка GCC-2) 9.2.0

Комментарии:

1. Почему это предположение? Сам язык не так уж много говорит о предположениях о фактической памяти (адресе), ограничения здесь очень абстрактны, и вам не стоит беспокоиться об этом. Хотя в C 11 есть некоторые расширения с точки зрения выравнивания и компоновки, но это уже другая тема.

2. @Secundi , спасибо. Но я хочу понять причину разницы, стоящей за этим. Также есть еще один момент, заключающийся в том, что G позволяет мне получить доступ к адресу памяти, к которому я не могу получить доступ через MSVC (ограниченный доступ)

3. Разница между компиляторами заключается в том, что разные компиляторы по-разному организуют память, даже если они создают программы, ориентированные на одну и ту же хост-систему. Стандарт C очень мало говорит о том, как переменные организованы в памяти, поэтому компиляторы вольны делать в основном то, что им нравится. И то, что делают разные компиляторы, зависит от того, как разработчик компилятора рассуждает о вещах (например, сопоставление исходного кода с исполняемым кодом).

Ответ №1:

Когда компилятор компилирует объектный файл, все адреса являются относительными. Затем компоновщик берет все эти адреса и помещает их в нужное место. Под правильным местом я подразумеваю, где операционная система ожидает, что они будут. (Подробности определяются зависящим от платформы сценарием компоновщика)

Когда вы загружаете исполняемый файл, операционная система помещает как ваш код (раздел .text в ELF), так и данные (разделы .data и .bss в ELF) по правильному адресу, а затем переходит на эти адреса.

В ветви программы (goto) адреса в исполняемом файле могут быть либо фиксированными, либо относительными (не зависящими от позиции). Если они исправлены, обычно исполняемый файл использует одни и те же адреса. Если они относительны, операционная система может загружаться в исполняемый файл по любому адресу, тогда ей придется настроить дополнительный код trampoline для корректной работы.

Для обеспечения безопасности операционной системы используйте рандомизацию расположения адресного пространства (ASLR). В этом режиме операционная система будет рандомизировать, где загружается материал. Это затрудняет использование дыр в безопасности. Как именно это работает, зависит от платформы.

Насколько я знаю, Linux на x86 использует фиксированные адреса, если вы не создаете свое распределение с -PIE помощью (независимого от позиции исполняемого файла.

Надеюсь, это даст вам лучшее представление о том, что происходит.

Ответ №2:

Компилятор выделяет память из доступного пула свободной памяти. Компьютеры обычно ведут список свободной и используемой памяти. Таким образом, вы будете получать один и тот же адрес после каждой компиляции. Таким образом, даже после изменения переменной naem вы получаете тот же адрес памяти.