Onde na memória minhas variáveis ​​estão armazenadas em C?

156

Considerando que a memória é dividida em quatro segmentos: dados, pilha, pilha e código, onde variáveis ​​globais, variáveis ​​estáticas, tipos de dados constantes, tipos de dados constantes, variáveis ​​locais (definidas e declaradas em funções), variáveis ​​(na função principal), ponteiros e o espaço alocado dinamicamente (usando malloc e calloc) são armazenados na memória?

Eu acho que eles seriam alocados da seguinte forma:

  • Variáveis ​​globais -------> dados
  • Variáveis ​​estáticas -------> dados
  • Tipos de dados constantes -----> código
  • Variáveis ​​locais (declaradas e definidas em funções) --------> stack
  • Variáveis ​​declaradas e definidas na função principal -----> heap
  • Ponteiros (por exemplo char *arr, int *arr) -------> heap
  • Espaço alocado dinamicamente (usando malloc e calloc) --------> stack

Estou me referindo a essas variáveis ​​apenas da perspectiva C.

Corrija-me se estiver errado, pois sou novo em C.

starkk92
fonte
4
Os tipos não são armazenados na memória.
5
mainé apenas outra função. Variáveis ​​vão para a pilha, a menos que seja malloccomo em qualquer outro lugar.
simonc
4
ponteiros são (geralmente) armazenados na pilha. A memória para a qual eles apontam (geralmente alocados via malloc / calloc) está (geralmente) no heap.
jpm
3
espaço alocado dinamicamente (usando malloc, calloc) --------> heap
One Man Crew
3
variáveis declaradas e definidas em função principal -----> pilha
Um Homem Tripulação

Respostas:

216

Você acertou algumas delas, mas quem escreveu as perguntas o enganou em pelo menos uma pergunta:

  • variáveis ​​globais -------> dados (correto)
  • variáveis ​​estáticas -------> dados (correto)
  • tipos de dados constantes -----> código e / ou dados. Considere literais de seqüência de caracteres para uma situação em que uma constante em si seria armazenada no segmento de dados e as referências a ela seriam incorporadas no código
  • variáveis ​​locais (declaradas e definidas em funções) --------> stack (correto)
  • variáveis ​​declaradas e definidas na mainfunção -----> heap também empilham (o professor estava tentando enganá-lo)
  • ponteiros (ex: char *arr, int *arr) -------> pilha dados ou pilha, dependendo do contexto. C permite declarar um staticponteiro global ou um ponteiro; nesse caso, o ponteiro em si acabaria no segmento de dados.
  • alocada dinamicamente espaço (usando malloc, calloc, realloc) --------> pilha pilha

Vale ressaltar que a "pilha" é oficialmente chamada de "classe de armazenamento automático".

dasblinkenlight
fonte
6
Também vale mencionar que o heap oficialmente não é chamado de nada. A memória alocada vem de algum lugar, não há nome no padrão para esse "lugar".
21713 Steve Steveop
6
Em alguns sistemas, (Linux e * BSD), também existe um allocaque funciona de maneira semelhante malloc, mas faz a alocação de pilha.
Andreas Grapentin
Para onde vai a variável const declarada dentro de um método?
Mahori
@ Ravi O mesmo lugar que o resto das constantes vai (ponto 3 acima).
dasblinkenlight
Estou usando o GCC 4.8.1 e ele não parece armazenar uma variável const local para principal no segmento DATA. Abaixo está o mapa de código e memória para três desses programas: Código 1: int main (void) {// char a [10] = "HELLO"; // 1 // const char a [10] = "HELLO"; // 2 return 0; } MAPA DE MEMÓRIA PARA ACIMA: nome do arquivo de dados bss dec hex nome do arquivo 7264 1688 1040 9992 2708 a.exe MAPA DE MEMÓRIA PARA 2: nome do arquivo bss de dados hexadecimais 7280 1688 1040 10008 2718 MAPA DE MEMÓRIA PARA 3: texto dados bss dec hex nome do arquivo 7280 1688 1040 10008 2718 a.exe
Mahori
124

Para os futuros visitantes que possam estar interessados ​​em conhecer esses segmentos de memória, estou escrevendo pontos importantes sobre 5 segmentos de memória em C:

Alguns heads-up:

  1. Sempre que um programa C é executado, alguma memória é alocada na RAM para a execução do programa. Essa memória é usada para armazenar o código executado com freqüência (dados binários), variáveis ​​de programa, etc. Os segmentos de memória abaixo falam sobre o mesmo:
  2. Normalmente, existem três tipos de variáveis:
    • Variáveis ​​locais (também chamadas como variáveis ​​automáticas em C)
    • Variáveis ​​globais
    • Variáveis ​​estáticas
    • Você pode ter variáveis ​​estáticas globais ou estáticas locais, mas as três acima são os tipos principais.

5 segmentos de memória em C:

1. Segmento de código

  • O segmento de código, também conhecido como segmento de texto, é a área da memória que contém o código executado com freqüência.
  • O segmento de código geralmente é somente leitura para evitar o risco de ser substituído por erros de programação, como buffer overflow, etc.
  • O segmento de código não contém variáveis ​​de programa como variável local ( também chamada de variáveis ​​automáticas em C ), variáveis ​​globais etc.
  • Com base na implementação C, o segmento de código também pode conter literais de seqüência de caracteres somente leitura. Por exemplo, quando você faz a printf("Hello, world")sequência "Olá, mundo" é criada no segmento de código / texto. Você pode verificar isso usando o sizecomando no Linux OS.
  • Leitura adicional

Segmento de dados

O segmento de dados é dividido nas duas partes abaixo e geralmente fica abaixo da área de pilha ou em algumas implementações acima da pilha, mas o segmento de dados nunca fica entre a área de pilha e pilha.

2. Segmento de dados não inicializado

  • Este segmento também é conhecido como bss .
  • Esta é a parte da memória que contém:
    1. Variáveis ​​globais não inicializadas (incluindo variáveis ​​de ponteiro)
    2. Variáveis ​​globais constantes não inicializadas .
    3. Variáveis ​​estáticas locais não inicializadas .
  • Qualquer variável local global ou estática que não seja inicializada será armazenada no segmento de dados não inicializado
  • Por exemplo: variável global int globalVar;ou variável local estáticastatic int localStatic; será armazenada no segmento de dados não inicializado.
  • Se você declarar uma variável global e a inicializar como 0ou NULLainda assim, ela será direcionada para o segmento de dados não inicializado ou bss.
  • Leitura adicional

3. Segmento de dados inicializado

  • Este segmento armazena:
    1. Variáveis ​​globais inicializadas (incluindo variáveis ​​de ponteiro)
    2. Variáveis ​​globais constantes constantes inicializadas .
    3. Variáveis ​​estáticas locais inicializadas .
  • Por exemplo: variável global int globalVar = 1;ou variável local estática static int localStatic = 1;será armazenada no segmento de dados inicializado.
  • Esse segmento pode ser classificado ainda mais na área de somente leitura inicial e na área de leitura e gravação inicializada . As variáveis ​​globais constantes e inicializadas vão para a área somente leitura inicial, enquanto as variáveis ​​cujos valores podem ser modificados em tempo de execução vão para a área de leitura e gravação inicializada .
  • O tamanho desse segmento é determinado pelo tamanho dos valores no código-fonte do programa e não é alterado no tempo de execução .
  • Leitura adicional

4. Segmento de pilha

  • O segmento de pilha é usado para armazenar variáveis ​​criadas dentro de funções (a função pode ser a principal ou definida pelo usuário ), variáveis ​​como
    1. Variáveis ​​locais da função (incluindo variáveis ​​de ponteiro)
    2. Argumentos passados ​​para a função
    3. Endereço de devolução
  • As variáveis ​​armazenadas na pilha serão removidas assim que a execução da função terminar.
  • Leitura adicional

5. Segmento de Heap

  • Este segmento é para oferecer suporte à alocação dinâmica de memória. Se o programador quer alocar alguma memória dinamicamente, em seguida, C é feito usando o malloc, callocourealloc métodos.
  • Por exemplo, quando int* prt = malloc(sizeof(int) * 2)então oito bytes serão alocados no heap e o endereço de memória desse local será retornado e armazenado na ptrvariável optr variável estará na pilha ou no segmento de dados, dependendo da maneira como é declarada / usada.
  • Leitura adicional
hagrawal
fonte
Isso não deveria ser inicializado em vez de não inicializado em 3. Segmento de dados inicializado.
Suraj Jain
Re "armazenado no segmento de dados não inicializado" (várias instâncias): você quer dizer "armazenado não inicializado no segmento de dados" ?
Peter Mortensen
@PeterMortensen, quero dizer as duas coisas. "Qualquer variável local global ou estática que não seja inicializada será armazenada no segmento de dados não inicializados"
hagrawal
como podemos ter variável estática global em C?
em "algumas dicas", achei este ponto "Você pode ter variáveis ​​estáticas globais ou estáticas locais, mas as três acima são os tipos principais". em que você se referiu ao termo "estática global". Meu argumento é que uma variável estática não pode ser global. ou seja, se alguma variável precisar ser global, ela deverá estar acessível até que a execução do programa seja concluída. Por favor, explique e ajude se eu estiver errado.
11

Corrigiu suas frases erradas

constant data types ----->  code //wrong

variáveis ​​constantes locais -----> stack

variável constante global inicializada -----> segmento de dados

variável constante global não inicializada -----> bss

variables declared and defined in main function  ----->  heap //wrong

variáveis ​​declaradas e definidas na função principal -----> stack

pointers(ex:char *arr,int *arr) ------->  heap //wrong

dynamically allocated space(using malloc,calloc) --------> stack //wrong

ponteiros (ex: char * arr, int * arr) -------> o tamanho dessa variável de ponteiro estará na pilha.

Considere que você está alocando memória de n bytes (usando mallocou calloc) dinamicamente e, em seguida, tornando o ponteiro variável para apontá-lo. Agora que os nbytes de memória estão no heap e a variável do ponteiro solicita 4 bytes (se a máquina de 64 bits tiver 8 bytes), que estarão na pilha para armazenar o ponteiro inicial dos nbytes do bloco de memória.

Nota: As variáveis ​​de ponteiro podem apontar a memória de qualquer segmento.

int x = 10;
void func()
{
int a = 0;
int *p = &a: //Now its pointing the memory of stack
int *p2 = &x; //Now its pointing the memory of data segment
chat *name = "ashok" //Now its pointing the constant string literal 
                     //which is actually present in text segment.
char *name2 = malloc(10); //Now its pointing memory in heap
...
}

espaço alocado dinamicamente (usando malloc, calloc) --------> heap

rashok
fonte
ponteiros pode ser em qualquer pilha ou a pilha (ver especialmente: ponteiros para ponteiros)
argentage
@airza: Agora atualizado. Na verdade eu estava atualizando apenas que os detalhes :)
rashok
No mapa de memória a seguir, você poderia apontar onde está a pilha e a pilha? Não tenho certeza se essa é a pergunta correta, pois a pilha e a memória podem ser aplicáveis ​​apenas em tempo de execução. MAPA DE MEMÓRIA: "texto data bss dec hex nome do arquivo 7280 1688 1040 10008 2718 a.exe"
Mahori 26/06
7

Uma arquitetura de desktop popular divide a memória virtual de um processo em vários segmentos :

  • Segmento de texto: contém o código executável. O ponteiro de instruções aceita valores nesse intervalo.

  • Segmento de dados: contém variáveis ​​globais (ou seja, objetos com ligação estática). Subdividido em dados somente leitura (como constantes de seqüência de caracteres) e dados não inicializados ("BSS").

  • Segmento de pilha: contém a memória dinâmica do programa, ou seja, o armazenamento gratuito ("heap") e os quadros de pilha locais para todos os threads. Tradicionalmente, a pilha C e a pilha C costumavam crescer no segmento de pilhas de extremidades opostas, mas acredito que a prática foi abandonada porque é muito insegura.

O programa CA normalmente coloca objetos com duração de armazenamento estático no segmento de dados, objetos alocados dinamicamente no armazenamento gratuito e objetos automáticos na pilha de chamadas do encadeamento em que ele vive.

Em outras plataformas, como o antigo modo real x86 ou em dispositivos incorporados, as coisas podem obviamente ser radicalmente diferentes.

Kerrek SB
fonte
"Acredito que a prática foi abandonada porque é muito insegura" - e torna impossível implementar threads, desde então você precisa de mais de uma pilha por programa e nem todas podem estar no final :-)
Steve Jessop
@ SteveJessop: Sim, eu também estava pensando nisso. Mas os threads existem há muito tempo - não sei se todas as pilhas de threads também cresceram para trás, ou se cresceram como a pilha ... de qualquer maneira, hoje em dia tudo segue na mesma direção e há guarda Páginas.
Kerrek SB 29/01
6

Estou me referindo a essas variáveis ​​apenas da perspectiva C.

Do ponto de vista da linguagem C , tudo o que importa é extensão, escopo, vínculo e acesso; exatamente como os itens são mapeados para diferentes segmentos de memória depende da implementação individual e isso varia. O padrão de linguagem não fala sobre segmentos de memória em tudo . A maioria das arquiteturas modernas atua da mesma maneira; variáveis ​​de escopo de bloco e argumentos de função serão alocados da pilha, variáveis ​​de escopo de arquivo e estáticas serão alocadas de um segmento de dados ou código, memória dinâmica será alocada de uma pilha, alguns dados constantes serão armazenados em segmentos somente leitura , etc.

John Bode
fonte
3

Uma coisa que é preciso ter em mente sobre o armazenamento é a regra como se . O compilador não precisa colocar uma variável em um local específico - em vez disso, pode colocá-la onde quiser, desde que o programa compilado se comporte como se fosse executado na máquina C abstrata, de acordo com as regras da máquina C abstrata. Isso se aplica a todas as durações de armazenamento . Por exemplo:

  • uma variável que não é acessada pode ser completamente eliminada - ela não tem armazenamento ... em qualquer lugar. Exemplo - veja como existe 42o código de montagem gerado, mas nenhum sinal de 404.
  • uma variável com duração de armazenamento automático que não tenha o endereço escolhido não precisa ser armazenada na memória. Um exemplo seria uma variável de loop.
  • uma variável que está constou efetivamente constnão precisa estar na memória. Exemplo - o compilador pode provar que fooé eficaz conste enfatiza seu uso no código. barpossui ligação externa e o compilador não pode provar que não seria alterado fora do módulo atual, portanto, não está embutido.
  • um objeto alocado com mallocnão precisa residir na memória alocada do heap! Exemplo - observe como o código não tem uma chamada malloce nem o valor 42 é armazenado na memória, ele é mantido em um registro!
  • portanto, um objeto que foi alocado por malloce a referência é perdida sem desalocar o objeto sem free precisar vazar memória ...
  • o objeto alocado por mallocnão precisa estar dentro do heap abaixo da quebra do programa ( sbrk(0)) no Unixen ...
Antti Haapala
fonte
1

ponteiros (ex: char * arr, int * arr) -------> heap

Não, eles podem estar na pilha ou no segmento de dados. Eles podem apontar para qualquer lugar.

Steve Wellens
fonte
As declarações sobre maine variáveis dinamicamente alocados são errado também
simonc
Não apenas na pilha ou no segmento de dados. Pense em um ponteiro que aponte para uma variedade de ponteiros. Nesse caso, os ponteiros na matriz são armazenados na pilha.
Sebi2020
1
  • Variáveis ​​/ variáveis ​​automáticas ---> seção de pilha
  • Variáveis ​​alocadas dinamicamente ---> seção heap
  • Variáveis ​​globais inicializadas -> seção de dados
  • Variáveis ​​globais não inicializadas -> seção de dados (bss)
  • Variáveis ​​estáticas -> seção de dados
  • Constantes de string -> seção de texto / seção de código
  • Funções -> seção de texto / seção de código
  • Código de texto -> seção de texto / seção de código
  • Registradores -> Registradores de CPU
  • Entradas da linha de comando -> seção ambiental / linha de comando
  • Variáveis ​​ambientais -> seção ambiental / linha de comando
prashad
fonte
O que é a seção ambiental / linha de comando? Eles existem no Linux?
Haoyuan Ge
-1

Exemplos mínimos executáveis ​​do Linux com análise de desmontagem

Como esse é um detalhe da implementação não especificado pelos padrões, vamos apenas dar uma olhada no que o compilador está fazendo em uma implementação específica.

Nesta resposta, irei vincular a respostas específicas que fazem a análise ou fornecê-la diretamente aqui e resumir todos os resultados aqui.

Todos eles estão em várias versões do Ubuntu / GCC, e os resultados provavelmente são bastante estáveis ​​entre as versões, mas se encontrarmos alguma variação, vamos especificar versões mais precisas.

Variável local dentro de uma função

Seja ele mainou qualquer outra função:

void f(void) {
    int my_local_var;
}

Como mostrado em: O que <valor otimizado> significa em gdb?

  • -O0: pilha
  • -O3: registra se não derramar, empilhar de outra forma

Para obter motivação sobre o motivo da pilha existir, consulte: Qual é a função das instruções push / pop usadas nos registradores na montagem x86?

Variáveis ​​globais e staticvariáveis ​​de função

/* BSS */
int my_global_implicit;
int my_global_implicit_explicit_0 = 0;

/* DATA */
int my_global_implicit_explicit_1 = 1;

void f(void) {
    /* BSS */
    static int my_static_local_var_implicit;
    static int my_static_local_var_explicit_0 = 0;

    /* DATA */
    static int my_static_local_var_explicit_1 = 1;
}

char * e char c[]

Como mostrado em: Onde as variáveis ​​estáticas são armazenadas em C e C ++?

void f(void) {
    /* RODATA / TEXT */
    char *a = "abc";

    /* Stack. */
    char b[] = "abc";
    char c[] = {'a', 'b', 'c', '\0'};
}

TODO literais de string muito grandes também serão colocados na pilha? Ou .data? Ou a compilação falha?

Argumentos de função

void f(int i, int j);

Deve passar pela convenção de chamada relevante, por exemplo: https://en.wikipedia.org/wiki/X86_calling_conventions para X86, que especifica registros específicos ou locais de pilha para cada variável.

Então, como mostrado em O que <valor otimizado> significa em gdb? , -O0coloca tudo na pilha, enquanto -O3tenta usar os registros o máximo possível.

Se a função for incorporada, no entanto, elas serão tratadas como locais comuns.

const

Acredito que isso não faz diferença, porque você pode descartá-lo.

Por outro lado, se o compilador é capaz de determinar que alguns dados nunca são gravados, ele poderia, em teoria, colocá-lo .rodatamesmo que não seja const.

Análise TODO.

Ponteiros

Eles são variáveis ​​(que contêm endereços, que são números), iguais aos demais :-)

malloc

A questão não faz muito sentido malloc, pois mallocé uma função e em:

int *i = malloc(sizeof(int));

*i é uma variável que contém um endereço e, portanto, se enquadra no caso acima.

Quanto ao funcionamento do malloc internamente, quando você o chama, o kernel do Linux marca determinados endereços como graváveis ​​em suas estruturas de dados internas e, quando são tocados pelo programa inicialmente, ocorre uma falha e o kernel ativa as tabelas de páginas, o que permite o acesso. acontece sem segfaul: Como funciona a paginação x86?

Observe, no entanto, que isso é basicamente exatamente o que o execsyscall faz quando você tenta executar um executável: ele marca as páginas nas quais deseja carregar e grava o programa, veja também: Como o kernel obtém um arquivo binário executável em execução linux? Exceto que exectem algumas limitações extras sobre onde carregar (por exemplo, se o código não é realocável ).

O syscall exato usado mallocé mmapnas implementações modernas de 2020 e no passado brkfoi usado: malloc () usa brk () ou mmap ()?

Bibliotecas dinâmicas

Basicamente, é mmapeditado na memória: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710

variáveis ​​ambientais e main'sargv

Acima da pilha inicial: /unix/75939/where-is-the-environment-string-actual-stored TODO por que não em .data?

Ciro Santilli adicionou uma nova foto
fonte