Что на самом деле делает компилятор, когда мы объявляем статические переменные?

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

4 ответов


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

Если вы хотите узнать больше о различных сегментах в исполняемых файлах,этой Wikipedia запись является хорошей отправной точкой. Я тоже настоятельно рекомендую Главу 7 в компьютерные системы: взгляд программиста Рэндал Э. Брайант и Дэвид Р. О'Халларон.

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

изменить:

автор любезно попросил меня уточнить:

какова точка деления инициализированной переменной 0 на .bss и не 0 инициализировано .Дейта?

из раздела 7.4 в компьютерные системы: взгляд программиста на .BSS:

этот раздел не занимает фактического пространства в объектном файле; это просто заполнитель. Форматы объектных файлов различают инициализированные и неинициализированные переменные для эффективность космоса: неинициализированный переменные не должны занимать какое-либо фактическое дисковое пространство в объекте файл.

и Википедия:

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

подведем итог: это механизм для сохранения памяти.


типичные компиляторы C производят вывод сборки, который создает четыре "секции" памяти. Компоновщик / загрузчик обычно объединяет различные элементы, помеченные одним и тем же разделом, при загрузке программы в память. Наиболее распространенными разделами являются:

"текст": это фактический программный код. Он считается доступным только для чтения (например, компоновщик/загрузчик на некоторых машинах может поместить его в ПЗУ).

"данные": это просто выделенная область ОЗУ, с копированными начальными значениями из исполняемого файла. Загрузчик выделит память, а затем скопирует ее исходное содержимое.

"bss": то же, что и данные, но инициализируется нулями.

"stack": просто выделяется загрузчиком для своего стека программы.

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

переменные"Auto" обычно выделяются в стеке во время выполнения программы (хотя, если они очень большие, они могут быть выделены в куче). Они существуют только в рамках своего стека.


static переменные-это глобальные переменные с ограниченной областью действия. @user3386109

  1. static / глобальные переменные существуют в течение всего срока службы программы.
  2. static / global инициализируются при запуске программы либо:

    A. Если явно не инициализировать: к битовому шаблону 0.
    B. В противном случае к явному значению, такому как double x = 1.23;

  3. static объем переменных либо ограничено

    A. Если определено вне функции: область файла, только код в файле может "видеть" переменную.
    B. Если определено внутри функции: область блока: только код внутри блока может "видеть" переменную.

  4. существует только один экземпляр static переменная в пределах своей области, если нижняя область не определяет другую с тем же именем. Компилятор "знает", какая же именованная переменная для доступа, используя ближайшую область. Это не re-created или re-initialized, даже если внутри функции.

Примечание: с несколькими потоками, другие соображения - не показано.

static int fred = 11;
int sally = 21;

void foo2(void) {
  static int fred = 31;
  int sally = 41;
  printf("static %d non-static %d\n", fred++, sally++);
  {
    printf("static %d non-static %d\n", fred++, sally++);
    {
      static int fred = 51;
      int sally = 61;
      printf("static %d non-static %d\n", fred++, sally++);
    }
  }
}

int main(void) {
  printf("static %d non-static %d\n", fred++, sally++);
  foo2();
  printf("static %d non-static %d\n", fred++, sally++);
  foo2();
  return 0;
}

выход

static 11 non-static 21
static 31 non-static 41
static 32 non-static 42
static 51 non-static 61
static 12 non-static 22
static 33 non-static 41
static 34 non-static 42
static 52 non-static 61

этот код:

void function()
{
    static int var = 6;

    // Make something with this variable
    var++;
}

внутренне похоже на это:

int only_the_compiler_knows_this_actual_name = 6;

void function()
{
    // Make something with the variable
    only_the_compiler_knows_this_actual_name++;
}

другими словами, это своего рода" глобальная " переменная, имя которой, однако, не конфликтует ни с какой другой глобальной переменной.