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

Почему компиляторы C и С++ размещают явно инициализированные и инициализированные по умолчанию глобальные переменные в разных сегментах?

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

Я тестировал следующие программы на 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, так и s находятся в совершенно разных сегментах. Но если я удалю инициализатор (начальное значение 5 в этой программе) из переменной S, а затем запустил программу, он даст мне следующий результат.

Вывод:

0x488020 0x488024

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

Это поведение также совпадает с C.

#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

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

Вывод:

004053D0 004053D4

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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