Где хранятся статические переменные в C и C ++? - PullRequest
159 голосов
/ 18 сентября 2008

В каком сегменте (.BSS, .DATA и т. Д.) Исполняемого файла хранятся статические переменные, чтобы избежать конфликта имен? Например:


foo.c:                         bar.c:
static int foo = 1;            static int foo = 10;
void fooTest() {               void barTest() {
  static int bar = 2;            static int bar = 20;
  foo++;                         foo++;
  bar++;                         bar++;
  printf("%d,%d", foo, bar);     printf("%d, %d", foo, bar);
}                              }

Если я скомпилирую оба файла и свяжу их с основной, которая неоднократно вызывает fooTest () и barTest, операторы printf увеличиваются независимо. Имеет смысл, поскольку переменные foo и bar являются локальными для единицы перевода.

Но где выделено хранилище?

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

Ответы [ 16 ]

117 голосов
/ 18 сентября 2008

Куда ведут ваши статики, зависит от того, они инициализированы нулями или нет. Инициализированные нулями статические данные поступают в .BSS (Блок начинается с символа) , не Инициализированные нулями данные входят в .DATA

101 голосов
/ 28 июля 2012

Когда программа загружена в память, она организована в разные сегменты. Одним из сегментов является Сегмент данных . Сегмент данных далее подразделяется на две части:

Инициализированный сегмент данных: Здесь хранятся все глобальные, статические и постоянные данные.
Неинициализированный сегмент данных (BSS): Все неинициализированные данные хранятся в этом сегменте.

Вот схема, объясняющая эту концепцию:

enter image description here


вот очень хорошая ссылка, объясняющая эти понятия:

http://www.inf.udec.cl/~leo/teoX.pdf

31 голосов
/ 20 сентября 2008

Фактически, переменная является кортежем (хранилище, область, тип, адрес, значение):

storage     :   where is it stored, for example data, stack, heap...
scope       :   who can see us, for example global, local...
type        :   what is our type, for example int, int*...
address     :   where are we located
value       :   what is our value

Локальная область действия может означать локальную либо для единицы перевода (исходного файла), функции или блока в зависимости от того, где она определена. Чтобы сделать переменную видимой для более чем одной функции, она определенно должна находиться в области DATA или BSS (в зависимости от того, инициализирована она явно или нет, соответственно). Затем его масштабируют соответственно всем функциям или функциям в исходном файле.

21 голосов
/ 18 сентября 2008

Место хранения данных будет зависеть от реализации.

Однако значение static означает «внутренняя связь». Таким образом, символ является внутренним для модуля компиляции (foo.c, bar.c) и не может быть указан вне этого модуля компиляции. Таким образом, не может быть конфликтов имен.

13 голосов
/ 18 сентября 2008

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

Использование static внутри функции отличается - переменная видна только функции, ее значение сохраняется только при вызовах этой функции.

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

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

9 голосов

Как найти себя с помощью objdump -Sr

Чтобы действительно понять, что происходит, вы должны понимать перемещение компоновщика. Если вы никогда не касались этого, подумайте сначала прочитав это сообщение .

Давайте проанализируем пример Linux x86-64 ELF, чтобы увидеть его сами:

#include <stdio.h>

int f() {
    static int i = 1;
    i++;
    return i;
}

int main() {
    printf("%d\n", f());
    printf("%d\n", f());
    return 0;
}

Компилировать с:

gcc -ggdb -c main.c

Декомпилируйте код с помощью:

objdump -Sr main.o
  • -S декомпилирует код с исходным кодом, смешанным
  • -r показывает информацию о переезде

Внутри декомпиляции f мы видим:

 static int i = 1;
 i++;
4:  8b 05 00 00 00 00       mov    0x0(%rip),%eax        # a <f+0xa>
        6: R_X86_64_PC32    .data-0x4

и .data-0x4 говорит, что он перейдет к первому байту сегмента .data.

-0x4 есть, потому что мы используем относительную адресацию RIP, то есть %rip в инструкции и R_X86_64_PC32.

Требуется, потому что RIP указывает на следующую инструкцию, которая начинается через 4 байта после 00 00 00 00, что и будет перемещено. Я объяснил это более подробно по адресу: https://stackoverflow.com/a/30515926/895245

Затем, если мы изменим источник на i = 1 и проведем тот же анализ, мы заключим, что:

  • static int i = 0 продолжается .bss
  • static int i = 1 продолжается .data
8 голосов
/ 18 сентября 2008

в области "global and static":)

Есть несколько областей памяти в C ++

  • кучи
  • бесплатный магазин
  • стек
  • глобальный и статический
  • сопзЬ

см. здесь для подробного ответа на ваш вопрос

6 голосов
/ 18 сентября 2008

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

5 голосов
/ 19 сентября 2008

Данные, объявленные в модуле компиляции, попадут в .BSS или .Data этих файловых выходных данных. Инициализированные данные в BSS, неинициализированные в DATA.

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

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

2 голосов
/ 12 марта 2018

Вот как (легко понять):

stack, heap and static data

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...