Ответ 1
Куда идут ваши статики, зависит от того, инициализированы они нулем или нет. Инициализированные нулем статические данные помещаются в .BSS (блок, начатый символом), неинициализированные нулем данные попадают в .DATA
В каком сегменте (.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.
Куда идут ваши статики, зависит от того, инициализированы они нулем или нет. Инициализированные нулем статические данные помещаются в .BSS (блок, начатый символом), неинициализированные нулем данные попадают в .DATA
Когда программа загружается в память, ее организуют в разные сегменты. Один из сегментов - сегмент DATA. Сегмент данных далее подразделяется на две части:
Инициализированный сегмент данных: Здесь хранятся все глобальные, статические и постоянные данные.
Неинициализированный сегмент данных (BSS): Все неинициализированные данные сохраняются в этом сегменте.
Вот схема, объясняющая это понятие:
здесь очень хорошая ссылка, объясняющая эти понятия:
Фактически, переменная является кортежем (память, область действия, тип, адрес, значение):
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 (в зависимости от того, была ли она инициализирована явно или нет, соответственно). Затем он привязан к любой функции (ей) или функции (ей) в исходном файле.
Место хранения данных будет зависимым от реализации.
Однако значение static - это "внутренняя связь". Таким образом, этот символ является внутренним для модуля компиляции (foo.c, bar.c) и не может быть указан за пределами этого блока компиляции. Таким образом, столкновений имен не может быть.
Я не верю, что произойдет столкновение. Использование static на уровне файла (внешние функции) помещает переменную как локальную в текущий блок компиляции (файл). Он никогда не отображается за пределами текущего файла, поэтому никогда не должно быть имени.
Использование static внутри функции различно - переменная видна только функции, она просто сохраняет ее значение во всех вызовах этой функции.
По сути, статика выполняет две разные вещи в зависимости от того, где она находится. В одних случаях, однако, это ограничивает видимость переменной для предотвращения конфликтов пространства имен,
Сказав это, я считаю, что он будет храниться в DATA, который, как правило, имеет инициализированную переменную. BSS первоначально стояла за байтом-set-<something> , в котором содержались переменные, которые не были инициализированы.
Как найти его самостоятельно с помощью 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
в "глобальной и статической" области :)
в C++ имеется несколько областей памяти,
см здесь для подробного ответа на Ваш вопрос
Это зависит от используемой платформы и компилятора. Некоторые компиляторы хранятся непосредственно в сегменте кода. Статические переменные всегда доступны только для текущей единицы перевода, а имена не экспортируются, поэтому конфликты имен причин никогда не возникают.
Данные, объявленные в компиляционной единице, войдут в .BSS или .Data этого файла. Инициализированные данные в BSS, не инициализированные в DATA.
Разница между статическими и глобальными данными заключается в включении символьной информации в файл. Компиляторы обычно включают в себя информацию о символах, но только отмечают глобальную информацию как таковую.
Компонент уважает эту информацию. Информация о символах для статических переменных либо отбрасывается, либо искажается, поэтому статические переменные все равно могут быть связаны каким-либо образом (с вариантами отладки или символа). В любом случае не могут быть затронуты блоки компиляции, так как компоновщик сначала разрешает локальные ссылки.
статическая переменная, хранящаяся в сегменте данных или сегменте кода, как упоминалось ранее.
Вы можете быть уверены, что он не будет выделен на стек или кучу.
Нет риска столкновения, так как ключевое слово static
определяет область действия переменной как файл или функцию, в случае столкновения есть компилятор/компоновщик, чтобы предупредить вас.
Хороший пример
Ну, этот вопрос слишком стар, но так как никто не указывает какую-либо полезную информацию: Проверьте сообщение "mohit12379", объясняя хранилище статических переменных с тем же именем в таблице символов: http://www.geekinterview.com/question_details/24745
Я попробовал это с 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
Итак, это означает, что ваши четыре переменные расположены в событии раздела данных с тем же именем, но с другим смещением.
Ответ может очень сильно зависеть от компилятора, поэтому вы, вероятно, захотите отредактировать свой вопрос (я имею в виду, что даже понятие сегментов не предусмотрено ISO C и ISO С++). Например, в Windows исполняемый файл не содержит имен символов. Один "foo" будет смещен 0x100, другой, возможно, 0x2B0, а код из обеих единиц перевода компилируется, зная смещения для "их" foo.
Вот как (легко понять):
оба они будут сохранены независимо, однако, если вы хотите дать понять другим разработчикам, вы можете их обернуть в пространствах имен.
Если инициализирована статическая переменная Затем она будет храниться в памяти сегмента данных. Если статическая переменная не инициализирована (по умолчанию она равна нулю), то она хранится в BSS (блок, запущенный символом), который является неинтеллизированной памятью сегмента данных.
вы уже знаете, что он хранит в bss (запуск блока по символу), также называемый неинициализированным сегментом данных или в сегменте инициализированных данных.
позволяет взять простой пример
void main(void)
{
static int i;
}
вышеуказанная статическая переменная не инициализируется, поэтому она переходит в неинициализированный сегмент данных (bss).
void main(void)
{
static int i=10;
}
и, конечно, он инициализируется на 10, поэтому он переходит в инициализированный сегмент данных.