Onde são variáveis estáticas armazenadas em C e C++?
-
01-07-2019 - |
Pergunta
Em qual segmento (.BSS, .De DADOS, outros) de um arquivo executável são variáveis estáticas armazenadas de modo a que eles não têm nome colisão?Por exemplo:
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);
} }
Se eu compilar os arquivos e vinculá-lo a um principal que chama fooTest() e barTest repetidamente, o printf demonstrações incrementar de forma independente.Faz sentido, já que o foo e bar variáveis são locais para a unidade de tradução.
Mas onde está o armazenamento alocado?
Para ser claro, o pressuposto é que você tem um conjunto de ferramentas que seria um arquivo de saída no formato ELF.Assim, Eu acreditar que não tem para ser algum espaço reservado no arquivo executável para essas variáveis estáticas.
Para fins de discussão, vamos supor que usar o GCC toolchain.
Solução
Onde seus estática ir depende se eles são zero inicializado ou não. zero inicializado de dados estáticos vai em .bss (Bloco Iniciado por Symbol) , não zero inicializado de dados vai em .DATA
Outras dicas
Quando um programa é carregado na memória, é organizado em diferentes segmentos.Um segmento é Segmento de DADOS.O segmento de Dados é sub-dividido em duas partes:
Inicializado segmento de dados: Todas global, a estática e a constante de dados são armazenados aqui.
Não inicializada segmento de dados(BSS): Todos os dados não inicializados são armazenados neste segmento.
Aqui está um diagrama para explicar esse conceito:
aqui é muito bom link a explicar esses conceitos:
De facto, uma variável é tuplo (armazenamento, alcance, tipo, endereço, valor):
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
âmbito local poderia significar local ou a unidade de translação (arquivo de origem), a função ou o bloco, dependendo de onde a sua definido. Para tornar a variável visível a mais de uma função, ele definitivamente tem que ser em dados ou área de BSS (dependendo se a sua inicializada explicitamente ou não, respectivamente). Seu então escopo de acordo com qualquer toda a função (s) ou função (s) dentro do arquivo de origem.
O local de armazenamento dos dados será dependente de implementação.
No entanto, o significado de estática é "ligação interna". Assim, o símbolo é interno para a unidade de compilação (foo.c, bar.c) e não pode ser referenciado no exterior dessa unidade de compilação. Assim, não pode haver colisões de nomes.
Eu não acredito que haverá uma colisão. Usando estática no nível de arquivo (funções fora) marca a variável como local para a unidade de compilação atual (arquivo). É fora nunca mais visível o arquivo atual por isso nunca tem que ter um nome.
Usando estática dentro de uma função é diferente -. A variável é visível somente para a função, que é apenas o seu valor é preservada através de chamadas para essa função
Com efeito, estática faz duas coisas diferentes dependendo de onde ele é. Em OTH casos, no entanto, limita a visibilidade da variável para evitar confrontos de nomes,
Dito isto, creio que seria armazenado em dados, que tende a ter inicializado variável. O BSS originalmente significava byte-set-
Como encontrá-la-se com objdump -Sr
Para realmente entender o que está acontecendo, você deve entender vinculador deslocalização. Se você nunca tocou que, considere lendo este post primeiro .
Vamos analisar um exemplo Linux x86-64 ELF para vê-lo nós mesmos:
#include <stdio.h>
int f() {
static int i = 1;
i++;
return i;
}
int main() {
printf("%d\n", f());
printf("%d\n", f());
return 0;
}
Compilar com:
gcc -ggdb -c main.c
Decompile o código com:
objdump -Sr main.o
-
-S
decompiles o código com a fonte original entrelaçar - mostra
-r
informações de relocação
Dentro do decompilation de f
vemos:
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
eo .data-0x4
diz que ele vai para o primeiro byte do segmento .data
.
O -0x4
está lá porque nós estamos usando RIP endereçamento relativo, assim, a %rip
na instrução e R_X86_64_PC32
.
Ela é necessária porque RIP aponta para o seguinte instrução, que começa 4 bytes após 00 00 00 00
que é o que vai ser realocados. Tenho explicado isso com mais detalhes em: https://stackoverflow.com/a/30515926/895245
Então, se nós modificar a fonte para i = 1
e fazer a mesma análise, concluímos que:
-
static int i = 0
continua.bss
-
static int i = 1
continua.data
na área de "global e estática":)
existem vários área de memória em C ++
- pilha
- loja livre
- pilha
- Global & estática
- const
aqui para resposta detalhada à sua pergunta ??p>
Depende da plataforma e compilador que você está usando. Alguns compiladores armazenar diretamente no segmento de código. Variáveis ??estáticas são sempre acessíveis apenas para a unidade de tradução atual e os nomes não são exportados Assim, o nome colisões razão nunca ocorrem.
Os dados declarados em uma unidade de compilação vai para o .bss ou o .Data de que a saída de arquivos. dados inicializado em BSS, uninitalised em dados.
A diferença entre estática e dados globais vem na inclusão de informações símbolo no arquivo. Compiladores tendem a incluir as informações de símbolos, mas apenas marcar a informação global como tal.
O vinculador respeita esta informação. As informações símbolo para as variáveis ??estáticas é descartado ou mutilado para que variáveis ??estáticas ainda pode ser referenciado de alguma maneira (com opções de depuração ou símbolo). Em nenhum dos casos pode as unidades de compilação fica afectada como o vinculador resolve referências locais em primeiro lugar.
variável estática armazenada no segmento de dados ou segmento de código como mencionado antes.
Você pode ter certeza de que não serão alocados na pilha ou heap.
Não há risco de colisão desde static
palavra-chave definir o escopo da variável a ser um arquivo ou função, em caso de colisão não é um compilador / vinculador para avisá-lo sobre.
Um bom exemplo
Bem, esta pergunta é pouco velho demais, mas desde pontos ninguém out qualquer informação útil: Verifique a mensagem por 'mohit12379' explicando a loja de variáveis ??estáticas com o mesmo nome na tabela de símbolos: http://www.geekinterview.com/question_details/24745
Eu tentei com objdump e gdb, aqui está o resultado que eu recebo:
(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 <printf@plt>
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 <printf@plt>
0x00000000004005ad <+63>: pop %rbp
0x00000000004005ae <+64>: retq
End of assembler dump.
aqui é o resultado 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
Então, isso é para dizer, seus quatro variáveis ??estão localizados na seção de dados do evento o mesmo nome, mas com diferente offset.
A resposta pode muito bem depender do compilador, então você provavelmente vai querer editar a sua pergunta (quero dizer, mesmo a noção de segmentos não é exigida pelo ISO C nem ISO C ++). Por exemplo, no Windows um executável não carrega nomes de símbolo. Um 'foo' seria compensado 0x100, o outro talvez 0x2B0 e código de ambas as unidades de tradução é compilado saber os deslocamentos para o "seu" foo.
ambos estão indo para ser armazenada de forma independente, no entanto, se você quer deixar claro para outros desenvolvedores que você pode querer envolvê-los em namespaces.
você já sabe ou ele armazenar em BSS (bloco inicial pelo símbolo) também conhecida como segmento de dados não inicializada ou no segmento de dados inicializado.
Vamos dar um exemplo simples
void main(void)
{
static int i;
}
a variável estática acima não é inicializado, assim vai para o segmento de dados não inicializada (BSS).
void main(void)
{
static int i=10;
}
e é claro que inicializado por 10 por isso vai para segmento de dados inicializado.