Подтвердить что ты не робот

Локальные и статические переменные в 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 в первом случае, а не во втором.
4b9b3361

Ответ 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};

Размер исполняемого файла в последнем случае будет значительно больше