Где статические переменные хранятся в C и C++?

В каком сегменте (.BSS,.DATA, other) исполняемого файла хранятся статические переменные, так что они не имеют конфликтов имен? Например:


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.

Ответ 2

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

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

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

enter image description here


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

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

Ответ 3

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

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 (в зависимости от того, была ли она инициализирована явно или нет, соответственно). Затем он привязан к любой функции (ей) или функции (ей) в исходном файле.

Ответ 4

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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

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

#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, что и будет перенесено. Я объяснил это более подробно: fooobar.com/questions/20645/...

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

  • static int i = 0 продолжается .bss
  • static int i = 1 продолжается .data

Ответ 7

в "глобальной и статической" области :)

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

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

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

Ответ 8

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

Ответ 9

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

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

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

Ответ 10

статическая переменная, хранящаяся в сегменте данных или сегменте кода, как упоминалось ранее.
Вы можете быть уверены, что он не будет выделен на стек или кучу.
Нет риска столкновения, так как ключевое слово static определяет область действия переменной как файл или функцию, в случае столкновения есть компилятор/компоновщик, чтобы предупредить вас.
Хороший пример

Ответ 11

Ну, этот вопрос слишком стар, но так как никто не указывает какую-либо полезную информацию: Проверьте сообщение "mohit12379", объясняя хранилище статических переменных с тем же именем в таблице символов: http://www.geekinterview.com/question_details/24745

Ответ 12

Я попробовал это с objdump и gdb, вот результат, который я получаю:

(gdb) disas fooTest
Dump of assembler code for function fooTest:
   0x000000000040052d <+0>: push   %rbp
   0x000000000040052e <+1>: mov    %rsp,%rbp
   0x0000000000400531 <+4>: mov    0x200b09(%rip),%eax        # 0x601040 <foo>
   0x0000000000400537 <+10>:    add    $0x1,%eax
   0x000000000040053a <+13>:    mov    %eax,0x200b00(%rip)        # 0x601040 <foo>
   0x0000000000400540 <+19>:    mov    0x200afe(%rip),%eax        # 0x601044 <bar.2180>
   0x0000000000400546 <+25>:    add    $0x1,%eax
   0x0000000000400549 <+28>:    mov    %eax,0x200af5(%rip)        # 0x601044 <bar.2180>
   0x000000000040054f <+34>:    mov    0x200aef(%rip),%edx        # 0x601044 <bar.2180>
   0x0000000000400555 <+40>:    mov    0x200ae5(%rip),%eax        # 0x601040 <foo>
   0x000000000040055b <+46>:    mov    %eax,%esi
   0x000000000040055d <+48>:    mov    $0x400654,%edi
   0x0000000000400562 <+53>:    mov    $0x0,%eax
   0x0000000000400567 <+58>:    callq  0x400410 <[email protected]>
   0x000000000040056c <+63>:    pop    %rbp
   0x000000000040056d <+64>:    retq   
End of assembler dump.

(gdb) disas barTest
Dump of assembler code for function barTest:
   0x000000000040056e <+0>: push   %rbp
   0x000000000040056f <+1>: mov    %rsp,%rbp
   0x0000000000400572 <+4>: mov    0x200ad0(%rip),%eax        # 0x601048 <foo>
   0x0000000000400578 <+10>:    add    $0x1,%eax
   0x000000000040057b <+13>:    mov    %eax,0x200ac7(%rip)        # 0x601048 <foo>
   0x0000000000400581 <+19>:    mov    0x200ac5(%rip),%eax        # 0x60104c <bar.2180>
   0x0000000000400587 <+25>:    add    $0x1,%eax
   0x000000000040058a <+28>:    mov    %eax,0x200abc(%rip)        # 0x60104c <bar.2180>
   0x0000000000400590 <+34>:    mov    0x200ab6(%rip),%edx        # 0x60104c <bar.2180>
   0x0000000000400596 <+40>:    mov    0x200aac(%rip),%eax        # 0x601048 <foo>
   0x000000000040059c <+46>:    mov    %eax,%esi
   0x000000000040059e <+48>:    mov    $0x40065c,%edi
   0x00000000004005a3 <+53>:    mov    $0x0,%eax
   0x00000000004005a8 <+58>:    callq  0x400410 <[email protected]>
   0x00000000004005ad <+63>:    pop    %rbp
   0x00000000004005ae <+64>:    retq   
End of assembler dump.

вот результат objdump

Disassembly of section .data:

0000000000601030 <__data_start>:
    ...

0000000000601038 <__dso_handle>:
    ...

0000000000601040 <foo>:
  601040:   01 00                   add    %eax,(%rax)
    ...

0000000000601044 <bar.2180>:
  601044:   02 00                   add    (%rax),%al
    ...

0000000000601048 <foo>:
  601048:   0a 00                   or     (%rax),%al
    ...

000000000060104c <bar.2180>:
  60104c:   14 00                   adc    $0x0,%al

Итак, это означает, что ваши четыре переменные расположены в событии раздела данных с тем же именем, но с другим смещением.

Ответ 13

Ответ может очень сильно зависеть от компилятора, поэтому вы, вероятно, захотите отредактировать свой вопрос (я имею в виду, что даже понятие сегментов не предусмотрено ISO C и ISO С++). Например, в Windows исполняемый файл не содержит имен символов. Один "foo" будет смещен 0x100, другой, возможно, 0x2B0, а код из обеих единиц перевода компилируется, зная смещения для "их" foo.

Ответ 14

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

стек, куча и статические данные

Ответ 15

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

Ответ 16

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

Ответ 17

вы уже знаете, что он хранит в bss (запуск блока по символу), также называемый неинициализированным сегментом данных или в сегменте инициализированных данных.

позволяет взять простой пример

void main(void)
{
static int i;
}

вышеуказанная статическая переменная не инициализируется, поэтому она переходит в неинициализированный сегмент данных (bss).

void main(void)
{
static int i=10;
}

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