2013-04-20 2 views
6

Я использую gcc версии 4.7.2 на Ubuntu 12.10 x86_64.Управление памятью в gcc

Прежде всего, это размеры типов данных на моем терминале:

sizeof(char) = 1  

sizeof(short) = 2   sizeof(int) = 4 
sizeof(long) = 8   sizeof(long long) = 8 

sizeof(float) = 4   sizeof(double) = 8 
sizeof(long double) = 16 

Теперь, пожалуйста, посмотрите на этот фрагмент кода:

int main(void) 
{ 
    char c = 'a'; 
    printf("&c = %p\n", &c); 
    return 0; 
} 

Если я не ошибаюсь, мы можем» t предсказывать что-нибудь об адресе c. Но каждый раз эта программа дает случайный шестнадцатеричный адрес, заканчивающийся f. Таким образом, следующим доступным местоположением будет некоторое шестнадцатеричное значение, заканчивающееся на 0. Я наблюдал этот шаблон и в случае других типов данных. Для значения int адрес имел некоторое шестнадцатеричное значение, заканчивающееся на c. Для double это было некоторое случайное шестнадцатеричное значение, заканчивающееся на 8 и так далее.

У меня есть 2 вопроса.

1) Кто управляет таким распределением памяти? Это стандарт gcc или C?

2) Кто бы это ни был, почему это так? Почему переменная хранится таким образом, что следующая доступная ячейка памяти начинается с шестнадцатеричного значения, заканчивающегося 0? Любая конкретная выгода?

Теперь, пожалуйста, взгляните на этом фрагменте кода:

int main(void) 
{ 
    double a = 10.2; 
    int b = 20; 
    char c = 30; 
    short d = 40; 

    printf("&a = %p\n", &a); 
    printf("&b = %p\n", &b); 
    printf("&c = %p\n", &c); 
    printf("&d = %p\n", &d); 

    return 0; 
} 

Теперь вот что я наблюдал это совершенно новое для меня. Я думал, что переменная будет храниться в том же порядке, в котором они объявлены. Но нет! Это не так. Вот пример вывода одного случайного запуска:

&a = 0x7fff8686a698 
&b = 0x7fff8686a694 
&c = 0x7fff8686a691 
&d = 0x7fff8686a692 

кажется, что переменные упорядочиваются в порядке возрастания их размеров, а затем они хранятся в том же порядке сортировки, но с сохранением наблюдения 1. т.е. последний переменная (самая большая) хранится таким образом, что следующая доступная ячейка памяти является шестнадцатеричным значением, заканчивающимся на 0.

Вот мои вопросы:

3) Кто за этим? Это стандарт gcc или C?

4) Зачем тратить время на сортировку переменных, а затем выделение памяти вместо того, чтобы напрямую распределять память на основе «первым пришел первым»? Любое конкретное преимущество такого рода сортировки, а затем распределения памяти?

Теперь, пожалуйста, взгляните на этом фрагменте кода:

int main(void) 
{ 
    char array1[] = {1, 2}; 
    int array2[] = {1, 2, 3}; 

    printf("&array1[0] = %p\n", &array1[0]); 
    printf("&array1[1] = %p\n\n", &array1[1]); 

    printf("&array2[0] = %p\n", &array2[0]); 
    printf("&array2[1] = %p\n", &array2[1]); 
    printf("&array2[2] = %p\n", &array2[2]); 

    return 0; 
} 

Теперь это также шокирующим для меня. То, что я заметил, что массив всегда хранится в некоторой случайной шестнадцатеричное значение, оканчивающиеся на «0», если elements of an array >= 2 и если elements < 2 то он получает место в памяти следующее наблюдение 1.

Так вот мои вопросы:

5) Кто стоит за этим хранением массива при некотором случайном шестнадцатеричном значении, заканчивающемся на 0? Это стандарт gcc или C?

6) Теперь зачем тратить память? Я имею в виду, что array2 может быть сохранен сразу после array1 (и, следовательно, array2 будет иметь ячейку памяти, которая заканчивается на 2). Но вместо этого array2 хранится при следующем шестнадцатеричном значении, заканчивающемся на 0, тем самым оставляя между ними 14 ячеек памяти. Какие-либо конкретные преимущества?

+2

В действительности не задействованы никакие «стандарты». Это может зависеть от версии используемого компилятора, ABI, ядра, среды, в которой вы запускаете эту программу, и т. Д. ABI, вероятно, требует, чтобы указатель стека выравнивался по 16 байт, а компилятор помещал переменные в «как он хочет ». И 'array2' должен быть выровнен по слову (несколько из 4 байтов), в то время как' array1' не нужно ... –

+1

Почему номера комнат на этой стороне коридора все * даже *? Почему номера номеров на противоположной стороне коридора все * нечетные *? – wildplasser

+0

Почему именно вы спрашиваете? В чем причина вашего вопроса, и почему вам все равно? (Вы не должны кодировать программы, которые так сильно зависят от точной причины этого). –

ответ

5

Часть причин дана спецификациями application binary interface (ABI) для вашей системы &.

См. x86 calling conventions и SVR4 x86-64 ABI supplement (Я даю URL-адрес недавней копии, последний оригинал на удивление трудно найти в Интернете).

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

Компилятор пытается поместить локальные переменные в расположение стека с подходящим выравниванием. См. Расширение 0CCGCC. Где именно компилятор поставил эти переменные, это не важно, см. my answer here. (Если это важно для вашего кода, вы действительно должны упаковать переменные в один общий локальный struct, поскольку каждый флажок для компилятора, версии и оптимизации может делать разные вещи, поэтому не зависит от этого точного поведения вашего конкретного компилятора).

7

Адрес, в котором стек и начало кучи передается процессу операционной системой. Все остальное определяется компилятором, используя смещения, которые известны во время компиляции. Некоторые из этих вещей могут следовать существующей конвенции, используемой в вашей целевой архитектуре, а некоторые из них - нет.

В стандарте C ничего не говорится о порядке локальных переменных внутри фрейма стека (как указано в комментарии, он даже не предусматривает использование стека вообще). Стандарт только мешает определить порядок, когда дело доходит до структур, и даже тогда он не определяет конкретные смещения, а только тот факт, что эти смещения должны быть в порядке возрастания. Обычно компиляторы пытаются выровнять переменные таким образом, что доступ к ним занимает как можно меньше инструкций ЦП, и стандарт позволяет это, не требуя этого.

+0

Так что gcc находится за всеми вышеупомянутыми 3 наблюдениями. ОК. Итак, это отвечает на вопрос 1), 3) и 5). И компилятор делает такое распределение, чтобы сократить время получения. Но это спокойное общее утверждение. 1) Я имею в виду, как именно это помогает компилятору, если переменные сортируются в порядке возрастания их размеров, а затем выделяют память? 2) Как это уменьшает время выборки или какую бы пользу оно ни приносило, если переменные хранятся таким образом, что следующая доступная ячейка памяти представляет собой случайное шестнадцатеричное значение, заканчивающееся на '0'? – rootkea

+2

@rootkea - Бывает, что на вашем процессоре (x86) выборка данных выполняется быстрее всего, если адрес является даже кратным размеру данных. Таким образом, информация может перемещаться прямо в аппаратном обеспечении без каких-либо возможных задержек, необходимых для сортировки бит. Например, если ваш 'double' находится на адресе, заканчивающемся на 4, процессору, возможно, придется прочитать два 64-битных слова и выбрать четыре байта из каждого чтения. Это может занять некоторое дополнительное время. –

+0

Стандарт C не определяет, что автоматические переменные выделяются в стеке. В большинстве случаев они есть, но им это не нужно. – wildplasser

Смежные вопросы