O que é armazenado na pilha e o que é armazenado na pilha?

Alguém pode explicar claramente, em termos de C, C ++ e Java. O que tudo vai na pilha e tudo o que acontece na pilha e quando é a alocação feita.

Até onde sei,

Todas as variables ​​locais, se primitivos, pointers ou variables ​​de referência por chamada de function estão em um novo quadro de pilha.

e qualquer coisa criada com new ou malloc vai no heap.

Estou confuso sobre algumas coisas.

As referências / primitivos que são membros de um object criado no heap também são armazenados no heap?

e os membros locais de um método que estão sendo criados recursivamente em cada quadro. Eles estão todos na pilha? Se sim, então essa memory de pilha é alocada em tempo de execução? também para literais, eles são parte do segmento de código? e sobre globais em C, estático em C ++ / Java e estático em C.

Estrutura de um programa na memory

A seguir está a estrutura básica de qualquer programa quando carregada na memory.

+--------------------------+ | | | command line | | arguments | | (argc and argv[]) | | | +--------------------------+ | Stack | | (grows-downwards) | | | | | | | | FREE | | SPACE | | | | | | | | | | (grows upwards) Heap | +--------------------------+ | | | Initialized data | | segment | | | +--------------------------+ | | | Initialized to | | Zero (BSS) | | | +--------------------------+ | | | Program Code | | | +--------------------------+ 

Poucos pontos a serem observados:

  • Segmento de dados
    • Segmento de dados inicializado (inicializado para inicializadores explícitos por programadores)
    • Segmento de dados não inicializados (inicializado para zero segmento de dados – BSS [Block Start with Symbol])
  • Segmento de código
  • Áreas Stack e Heap

Segmento de dados

O segmento de dados contém os dados globais e estáticos que são explicitamente inicializados pelos usuários que contêm os valores inicializados.

A outra parte do segmento de dados é chamada de BSS (porque os sistemas IBM antigos tinham esse segmento inicializado como zero). É a parte da memory em que o sistema operacional inicializa o bloco de memory para zeros. É assim que os dados globais não inicializados e a estática obtêm o valor padrão como zero. Esta área é fixa e tem tamanho estático.

A área de dados é separada em duas áreas com base na boot explícita porque as variables ​​que devem ser inicializadas podem ser inicializadas uma a uma. No entanto, as variables ​​que não foram inicializadas não precisam ser explicitamente inicializadas com 0s um por um. Em vez disso, o trabalho de inicializar a variável é deixado para o sistema operacional. Essa boot em massa pode reduzir bastante o tempo necessário para carregar o arquivo executável.

Principalmente o layout do segmento de dados está no controle do sistema operacional subjacente, ainda alguns carregadores dão controle parcial aos usuários. Esta informação pode ser útil em aplicações como sistemas embarcados.

Esta área pode ser endereçada e acessada usando pointers do código. As variables ​​automáticas têm sobrecarga na boot das variables ​​sempre que são necessárias e o código é necessário para executar essa boot. No entanto, as variables ​​na área de dados não têm essa sobrecarga de tempo de execução porque a boot é feita apenas uma vez e também no tempo de carregamento.

Segmento de código

O código do programa é a área de código onde o código executável está disponível para execução. Esta área também é de tamanho fixo. Isso pode ser acessado apenas por pointers de function e não por outros pointers de dados. Outra informação importante a ser observada aqui é que o sistema pode considerar essa área como área de memory somente leitura e qualquer tentativa de escrever nessa área leva a um comportamento indefinido.

Strings constantes podem ser colocadas no código ou na área de dados e isso depende da implementação.

A tentativa de gravar na área de código leva a um comportamento indefinido. Por exemplo (vou dar apenas exemplos baseados em C ) o seguinte código pode resultar em erro de execução ou até mesmo travar o sistema.

 int main() { static int i; strcpy((char *)main,"something"); printf("%s",main); if(i++==0) main(); } 

Empilhar e acumular áreas

Para execução, o programa usa duas partes principais, a pilha e o heap. Os frameworks de empilhamento são criados na pilha para funções e heap para alocação de memory dinâmica. A pilha e o heap são áreas não inicializadas. Portanto, o que quer que esteja na memory torna-se o valor inicial (lixo) dos objects criados naquele espaço.

Vamos ver um exemplo de programa para mostrar quais variables ​​são armazenadas onde,

 int initToZero1; static float initToZero2; FILE * initToZero3; // all are stored in initialized to zero segment(BSS) double intitialized1 = 20.0; // stored in initialized data segment int main() { size_t (*fp)(const char *) = strlen; // fp is an auto variable that is allocated in stack // but it points to code area where code of strlen() is stored char *dynamic = (char *)malloc(100); // dynamic memory allocation, done in heap int stringLength; // this is an auto variable that is allocated in stack static int initToZero4; // stored in BSS static int initialized2 = 10; // stored in initialized data segment strcpy(dynamic,”something”); // function call, uses stack stringLength = fp(dynamic); // again a function call } 

Ou considere um exemplo ainda mais complexo,

 // command line arguments may be stored in a separate area int main(int numOfArgs, char *arguments[]) { static int i; // stored in BSS int (*fp)(int,char **) = main; // points to code segment static char *str[] = {"thisFileName","arg1", "arg2",0}; // stored in initialized data segment while(*arguments) printf("\n %s",*arguments++); if(!i++) fp(3,str); } 

Espero que isto ajude!

Em C / C ++: variables ​​locais são alocadas no quadro de pilha atual (pertencentes à function atual). Se você alocar estaticamente um object, o object inteiro é alocado na pilha, incluindo todas as suas variables ​​de membro. Ao usar a recursion, com cada chamada de function, um novo quadro de pilha é criado e todas as variables ​​locais são alocadas na pilha. A pilha geralmente tem tamanho fixo, e esse valor é geralmente escrito no header binário executável durante a compilation / vinculação. No entanto, isso é muito específico do sistema operacional e da plataforma; alguns sistemas operacionais podem aumentar a pilha dinamicamente quando necessário. Como o tamanho da pilha geralmente é limitado, você pode ficar sem pilha ao usar a recursion profunda ou, às vezes, mesmo sem recursion, ao alocar estaticamente objects grandes.

O heap geralmente é considerado como um espaço ilimitado (limitado apenas pela memory física / virtual disponível) e é possível alocar objects no heap usando malloc / new (e outras funções de alocação de heap). Quando um object é criado no heap, todas as suas variables ​​de membro são criadas dentro dele. Você deve ver um object como uma área contínua de memory (essa área contém variables ​​de membro e um ponteiro para uma tabela de método virtual), não importa onde esteja alocada.

Literais, constantes e outras coisas “fixas” são geralmente compiladas / ligadas ao binário como outro segmento, então não é realmente o segmento de código. Normalmente você não pode alocar ou liberar nada deste segmento em tempo de execução. No entanto, isso também é específico da plataforma, pode funcionar de forma diferente em diferentes plataformas (por exemplo, o código iOS Obj-C tem muitas referências constantes inseridas diretamente no segmento de código, entre funções).

Em C e C ++, pelo menos, isso é tudo específico da implementação. Os padrões não mencionam “pilha” ou “heap”.

Em Java, variables ​​locais podem ser alocadas na pilha (a menos que sejam otimizadas)

Primitivos e referências em um object estão no heap (como o object está no heap)

Uma pilha é pré-alocada quando o segmento é criado. Não usa espaço de heap. (No entanto, a criação de um encadeamento resulta na criação de um buffer de alocação local de encadeamento, o que diminui bastante a memory livre)

Literais de string exclusivos são adicionados ao heap. literais primitivos podem estar no código em algum lugar (se não forem otimizados) Se um campo é estático ou não, não faz diferença.

A Seção 3.5 da Especificação de Java Virtual Machine descreve as áreas de dados de tempo de execução (pilhas e heap).

Nem os padrões de linguagem C nem C ++ especificam se algo deve ser armazenado em uma pilha ou heap. Eles definem apenas a vida útil do object, visibilidade e modificabilidade; cabe à implementação mapear esses requisitos para o layout de memory de uma determinada plataforma.

Normalmente , qualquer coisa alocada com as funções *alloc reside no heap, enquanto variables auto e parâmetros de function residem em uma pilha. Os literais de string podem estar “em algum outro lugar” (eles devem estar alocados e visíveis durante o tempo de vida do programa, mas a tentativa de modificá-los é indefinida); algumas plataformas usam um segmento de memory separado, somente leitura, para armazená-las.

Apenas lembre-se de que existem algumas plataformas verdadeiramente excêntricas que podem não estar de acordo com o modelo comum de heap de pilha.

Para responder a parte da sua pergunta sobre heap e pilha C ++:

Devo dizer primeiro que um object criado sem novo é armazenado como uma unidade contígua na pilha ou se for global em algum segmento global (específico da plataforma).

Para um object que é criado usando o novo no heap, suas variables ​​de membro são armazenadas como um bloco contíguo de memory no heap. Esse é o caso das variables ​​de membro que são objects primitivos e incorporados. No caso de vars de membro que são pointers e variables ​​de membro de tipo de referência, um valor de ponteiro primitivo é armazenado dentro do object. O valor para o qual esse valor aponta pode ser armazenado em qualquer lugar (heap, pilha, global). Tudo é possível.

Quanto às variables ​​locais dentro dos methods de um object, elas são armazenadas na pilha, não dentro do espaço contíguo do object no heap. A pilha geralmente é criada de um tamanho fixo em tempo de execução. Existe um por thread. As variables ​​locais podem nem mesmo consumir espaço na pilha, pois podem ser otimizadas a partir dele (como disse Paul). O ponto principal é que elas não estão no heap apenas porque são funções membro de um object no heap. Se forem variables ​​locais de um tipo de ponteiro, elas podem ser armazenadas na pilha e apontar para algo no heap ou na pilha!