2015-12-19 3 views
24

Я читал this great post about memory layout of C programs. В нем говорится, что инициализированные глобальные переменные по умолчанию находятся в BSS segment, и если вы явно предоставляете значение глобальной переменной, то оно будет находиться в data segment.Почему компиляторы C и C++ помещают явно инициализированные и инициализированные по умолчанию глобальные переменные в разных сегментах?

Я проверил следующие программы на C и C++, чтобы изучить это поведение.

#include <iostream> 
// Both i and s are having static storage duration 
int i;  // i will be kept in the BSS segment, default initialized variable, default value=0 
int s(5); // s will be kept in the data segment, explicitly initialized variable, 
int main() 
{ 
    std::cout<<&i<<' '<<&s; 
} 

Выход:

0x488020 0x478004 

Таким образом, с выходом она явно выглядит как переменная I & сек находится в совершенно разных сегментах. Но если я удаляю инициализатор (начальное значение 5 в этой программе) из переменной S, а затем запускаю программу, он дает мне следующий результат.

Выход:

0x488020 0x488024 

Таким образом, с выхода он явно выглядит как переменные я и s находится в том же (в этом случае BSS) сегмента.

Такое поведение также же в С.

#include <stdio.h> 
int i;  // i will be kept in the BSS segment, default initialized variable, default value=0 
int s=5; // s will be kept in the data segment, explicitly initialized variable, 
int main(void) 
{ 
    printf("%p %p\n",(void*)&i,(void*)&s); 
} 

Выход:

004053D0 00403004 

Итак, опять же, мы можем сказать, глядя на выходе (средства, рассматривающим адрес переменных), как переменная i и s находится в совершенно разных сегментах. Но опять же, если я удаляю инициализатор (начальное значение 5 в этой программе) из переменной S, а затем запускаю программу, он дает мне следующий результат.

Выход:

004053D0 004053D4 

Таким образом, с выхода он явно выглядит как переменные я и s находится в том же (в этом случае BSS) сегмента.

Почему компиляторы C и C++ помещают явно инициализированные и инициализированные по умолчанию глобальные переменные в разных сегментах? Почему существует различие в том, где находится глобальная переменная между инициализированными и явно инициализированными переменными по умолчанию? Если я не ошибаюсь, стандарты C и C++ никогда не говорят о стеке, куче, сегменте данных, сегменте кода, сегменте BSS и обо всех таких вещах, которые специфичны для реализации. Итак, возможно ли, чтобы реализация C++ сохраняла явно инициализированные и инициализированные по умолчанию переменные в тех же сегментах, а не сохраняла их в разных сегментах?

+2

[.bss] (https://en.wikipedia.org/wiki/.bss): «Обычно в объектном файле хранится только длина раздела bss, но нет данных. Операционные системы могут используйте метод, называемый zero-fill-on-demand, чтобы эффективно реализовать сегмент bss « –

+1

Feel privileged ... Мне когда-то пришлось использовать компилятор, который ставил статические переменные в' .data', если бы они имели '= 0' initializer, и в '.bss', если у них не было инициализатора. А также пришлось использовать одну и ту же кодовую базу для другого (разбитого) компилятора, который не инициализировал нулевые статические переменные, у которых не было инициализатора. –

+0

Общая викторина: что означает BSS? –

ответ

27

Ни один из языков C или C++ не имеет понятия «сегменты», и не все операционные системы тоже, поэтому ваш вопрос неизбежно зависит от платформы и компилятора.

При этом общие реализации будут обрабатывать инициализированные или неинициализированные переменные по-разному. Основное отличие состоит в том, что неинициализированные (или по умолчанию 0-инициализированные) данные не должны быть фактически сохранены вместе с скомпилированным модулем, но только объявлено/зарезервировано для последующего использования во время выполнения. В практических терминах «сегмент» инициализированные данные : сохранены на диск как часть двоичного файла, а неинициализированные данные - , а не, вместо этого он выделяется при запуске, чтобы удовлетворить объявленные «оговорки».

11

По определению BSS не является отдельным сегментом, он является частью сегмента данных.

В C и C++, статический выделенные объекты без явного инициализатора инициализируется к нулю, реализация может также назначить статический выделенные переменные и константы инициализируются со значением , состоящим исключительно из нулевых многозначных бит BSS section.

Причины хранить их в ПБСЕ есть те типы переменных с неинициализированными или значениями по умолчанию может быть получен во время выполнения, не теряя пространства в бинарных файлах, а не переменных, которые размещены в данном сегменте.

13

Действительно короткий ответ - «потому что он занимает меньше места». (Как отмечают другие, компилятор не должен этого делать!)

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

Для нулевых инициализированных глобалов нет причин хранить много нулей. Вместо этого просто сохраните размер всего набора данных за один размер. Поэтому вместо хранения 4132 байт нуля в секторе data существует только «BSS длиной 4132 байта» - и до OS/runtime устанавливается так, что он равен нулю. - в некоторых случаях время выполнения компилятора будет memset(BSSStart, 0, BSSSize) или аналогичным. Например, в Linux, вся «неиспользуемая» память заполняется нулем в любом случае, когда процесс создается, поэтому установка BSS на ноль - это просто вопрос выделения памяти в первую очередь.

И, конечно, более короткие исполняемые файлы имеют несколько преимуществ: меньше места на жестком диске, более быстрое время загрузки [дополнительный бонус, если ОС предварительно заполняет выделенную память нулем], быстрее компилирует компилятор/linker не нужно записывать данные на диск.

Таким образом, для этого существует вполне практическая причина.

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