Локальные и статические переменные в C
При компиляции:
// external definitions
int value1 = 0;
static int value2 = 0;
gcc-компилятор генерирует следующую сборку:
.globl value1
.bss
.align 4
.type value1, @object
.size value1, 4
value1:
.zero 4
.local value2
.comm value2,4,4
Однако, когда я инициализирует переменные значением, отличным от нуля, например:
// external definitions
int value1 = 1;
static int value2 = 1;
gcc-компилятор сгенерировал следующее:
.globl value1
.data
.align 4
.type value1, @object
.size value1, 4
value1:
.long 1
.align 4
.type value2, @object
.size value2, 4
value2:
.long 1
Мои вопросы:
- Почему в первом случае значения выделяются в сегменте bss, а во втором - в сегменте данных.
- Почему переменная value2 определяется как .local и .comm в первом случае, а не во втором.
Ответы
Ответ 1
Вообще говоря, раздел bss
содержит неинициализированные значения, а раздел data
содержит инициализированные значения. Однако gcc устанавливает значения, которые инициализируются нулем в разделе bss
вместо раздела data
, так как раздел bss
в любом случае обнуляется во время выполнения, не имеет смысла хранить нули в data
, это экономит дисковое пространство, от человека gcc:
-fno-zero-initialized-in-bss Если цель поддерживает раздел BSS, GCC по умолчанию ставит переменные, которые инициализируются нулем в BSS. Эта может сэкономить место в полученном коде. Эта опция отключает это потому что некоторые программы явно полагаются на переменные, идущие в раздел данных
Я не уверен, почему .comm
используется со статическим хранилищем, которое является локальным для объектного файла, оно обычно используется для объявления общих символов, которые, если не определены/инициализированы, должны быть объединены компоновщиком с символом, который имеют то же имя из других объектных файлов и почему он не используется во втором примере, потому что переменные инициализируются, из руководства as
.comm объявляет общий символ с именем. При связывании символ в одном объектном файле может быть объединен с определенным или общим символ с тем же именем в другом объектном файле
Ответ 2
Первый случай - это то, что вы инициализировали значения с нулем. Он является частью стандарта C (раздел 6.7.8), что глобальное целое число инициализируется с помощью 0, если ни один не указан. Таким образом, форматы файлов предусматривали сокращение двоичных файлов с помощью специального раздела, в который они попадают: bss
. Если вы посмотрите на некоторые из спецификацию ELF (на странице I-15), вы найдете следующее:
.bss В этом разделе содержатся неинициализированные данные, которые вносят вклад в программу образ памяти. По определению система инициализирует данные нулями когда программа начинает работать. Раздел не занимает файлового пространства, так как обозначенный типом раздела, SHT_NOBITS.
В первом случае компилятор сделал оптимизацию. Для сохранения инициализатора не нужно занимать место в фактическом двоичном файле, так как он может использовать сегмент bss
и получить тот, который вы хотите бесплатно.
Теперь факт, что у вас есть статичность, поступающая из внешнего источника, немного интересна (обычно это не делается). Однако в модуле, который компилируется, он не должен использоваться совместно с другими модулями и должен быть помечен .local
. Я подозреваю, что он делает это так, потому что для инициализатора не существует фактического значения.
Во втором примере, поскольку вы задали ненулевой инициализатор, он знает, что он находится в сегменте инициализированных данных data
. value1
выглядит очень похоже, но для value2
компилятору необходимо зарезервировать место для инициализатора. В этом случае его не нужно маркировать как .local
, потому что он может просто сложить значение и сделать с ним. Это не глобально, потому что для него нет инструкции .globl
.
BTW, http://refspecs.linuxbase.org/ - это хорошее место для посещения некоторых низкоуровневых деталей о бинарных форматах и т.д.
Ответ 3
BSS - это сегмент, содержащий данные, инициализированные во время выполнения, когда сегмент данных содержит данные, инициализированные в двоичном формате программы.
Теперь статические переменные всегда инициализируются независимо от того, выполняются ли они явно в программе или нет. Но есть две отдельные категории: инициализированная (DS) и неинициализированная (BSS) статика.
Все значения, присутствующие в BSS, - это те, которые не инициализируются в коде программы и, следовательно, инициализируются, когда программа загружается во время выполнения до 0 (если целое число), null для указателей и т.д.
Итак, когда вы инициализируетесь с помощью 0, значение переходит в BSS, где, как и любое другое назначенное значение, будет выделяться переменная в сегменте данных.
Интересным следствием этого является то, что размер данных, инициализированных в BSS, не будет включен в двоичный файл программы, где включается как тот, который входит в сегмент данных.
Попробуйте выделить большой статический массив и использовать его в программе. См. Размер исполняемого файла, если он не инициализирован явно в коде. Затем инициализируйте его ненулевыми значениями, например
static int arr[1000] = {2};
Размер исполняемого файла в последнем случае будет значительно больше