Intereting Posts
В чем разница между ssize_t и ptrdiff_t? демпинговые размеры структуры C из объектного файла ELF Является ли ключевое слово extern для функции, необходимой вообще в C? неявное int и неявное объявление функций с gcc-компилятором Win32: Диалоговое окно панели инструментов, похоже, никогда не фокусируется и заставляет основное окно работать медленно? Вычисление суммы целых чисел в массиве Поведение PROT_READ и PROT_WRITE с помощью mprotect Как MPI_IN_PLACE работает с MPI_Scatter? Как инициализировать переменные для struct sockaddr_in malloc катастрофически катастрофически Почему forking мой процесс заставляет файл читать бесконечно Это хорошая идея использовать VLA C99 по сравнению с malloc / free? C с неопределенными результатами, компилятор генерирует неверный код (с -O3) segmentation fault при печати массива в C Способ преобразования streamа байтов в stream пакетов в C89 на встроенном устройстве

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

Однако, когда i инициализирует переменные значением, отличным от нуля, например:

 // 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 

Мои вопросы:

  1. Почему в первом случае значения выделяются в сегменте bss, а во втором – в сегменте данных.
  2. Почему переменная value2 определяется как .local и .comm в первом случае, а не во втором.

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

-fno-zero-initialized-in-bss Если цель поддерживает раздел BSS, GCC по умолчанию ставит переменные, которые инициализируются нулем в BSS. Это может сэкономить место в полученном коде. Этот параметр отключает это поведение, потому что некоторые программы явно полагаются на переменные, идущие в раздел данных

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

.comm объявляет общий символ с именем symbol. При связывании общий символ в одном объектном файле может быть объединен с определенным или общим символом с тем же именем в другом объектном файле

Первый случай – это то, что вы инициализировали значения с нулем. Это часть стандарта C (раздел 6.7.8), что глобальное целое число инициализируется с помощью 0, если ни один не указан. Таким образом, форматы файлов предусматривали bss двоичных файлов за счет наличия специального раздела, в который они попадают: bss . Если вы посмотрите на некоторые из спецификаций ELF (на странице I-15), вы найдете следующее:

.bss В этом разделе содержатся неинициализированные данные, которые вносят вклад в образ памяти программы. По определению система инициализирует данные нулями при запуске программы. Раздел не занимает файлового пространства, как указано типом раздела, SHT_NOBITS.

В первом случае компилятор сделал оптимизацию. Для хранения инициализатора не нужно занимать место в фактическом двоичном bss , так как он может использовать сегмент bss и получить тот, который вы хотите бесплатно.

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

Во втором примере, поскольку вы указали ненулевой инициализатор, он знает, что он находится в инициализированных данных сегмента data . value1 выглядит очень похоже, но для value2 компилятор должен зарезервировать место для инициализатора. В этом случае его не нужно маркировать как .local потому что он может просто сложить значение и сделать с ним. Это не глобально, потому что для него нет инструкции .globl .

BTW, http://refspecs.linuxbase.org/ – хорошее место для посещения некоторых низкоуровневых деталей о бинарных форматах и ​​т. Д.

BSS – это сегмент, содержащий данные, инициализированные во время выполнения, когда сегмент данных содержит данные, инициализированные в двоичном коде программы .

Теперь статические переменные всегда инициализируются независимо от того, выполняются ли они явно в программе или нет. Но есть две отдельные категории: инициализированная (DS) и неинициализированная (BSS) статика.

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

Поэтому, когда вы инициализируетесь с помощью 0, значение переходит к BSS, где, как и любое другое назначенное значение, будет выделяться переменная в сегменте данных.

Интересным следствием является то, что размер данных, инициализированных в BSS, не будет включен в двоичный файл программы, где включен компонент данных в сегменте данных.

Попробуйте выделить большой статический массив и использовать его в программе. См. Размер исполняемого файла, если он не инициализирован явно в коде. Затем инициализируйте его ненулевыми значениями, например

 static int arr[1000] = {2}; 

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