Skip to content

Commit fc93cd8

Browse files
committed
14 - Stack e Heap
1 parent 9954ba2 commit fc93cd8

File tree

1 file changed

+131
-1
lines changed

1 file changed

+131
-1
lines changed

docs/tutorial/secao014.md

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,131 @@
1-
# 14 - Stack e Heap
1+
# 14 - Stack e Heap
2+
3+
A memória que um programa usa é normalmente dividida em algumas áreas diferentes, chamadas segmentos:
4+
5+
- O segmento de código (também chamado de segmento de texto), onde o programa compilado fica na memória. O segmento de código normalmente é somente leitura.
6+
- O segmento bss (também chamado de segmento de dados não inicializado), onde variáveis globais e estáticas inicializadas com zero são armazenadas.
7+
- O segmento de dados (também chamado de segmento de dados inicializado), onde as variáveis globais e estáticas inicializadas são armazenadas.
8+
- O heap, de onde variáveis alocadas dinamicamente são alocadas.
9+
- A stack(pilha) de chamadas, onde são armazenados os parâmetros da função, variáveis locais e outras informações relacionadas à função.
10+
Nesta lição, focaremos principalmente a heap e a stack, pois é aí que a maioria das coisas interessantes ocorre.
11+
12+
O segmento de heap (também conhecido como "armazenamento gratuito") controla a memória usada para alocação dinâmica de memória. Utilizamos ela na seção passada:
13+
14+
```cpp{0}
15+
int *ponteiro = new int; // ptr is assigned 4 bytes in the heap
16+
int *vetor = new int[10]; // array is assigned 40 bytes in the heap
17+
18+
delete ponteiro;
19+
delete vetor;
20+
```
21+
22+
Quando uma variável alocada dinamicamente é excluída, a memória é "retornada" para o heap e pode ser reatribuída à medida que futuras solicitações de alocação forem recebidas. Lembre-se de que excluir um ponteiro não exclui a variável, apenas retorna a memória no endereço associado ao sistema operacional.
23+
24+
A stack tem vantagens e desvantagens:
25+
26+
- A alocação de memória no heap é comparativamente lenta.
27+
- A memória alocada permanece alocada até que seja desalocada especificamente (cuidado com vazamentos de memória) ou o aplicativo termina (nesse ponto, o SO deve limpá-la).
28+
- A memória alocada dinamicamente deve ser acessada por meio de um ponteiro. Desreferenciar um ponteiro é mais lento do que acessar diretamente uma variável.
29+
Como o heap é um grande pool de memória, matrizes, estruturas ou classes grandes podem ser alocadas aqui.
30+
31+
# A stack de chamadas
32+
33+
A stack de chamadas (geralmente chamada de "a pilha") tem um papel muito mais interessante a desempenhar. A pilha de chamadas controla todas as funções ativas (aquelas que foram chamadas, mas ainda não foram finalizadas) desde o início do programa até o ponto de execução atual e lida com a alocação de todos os parâmetros de função e variáveis locais.
34+
35+
A pilha de chamadas é implementada como uma estrutura de dados da pilha. Portanto, antes de podermos falar sobre como a pilha de chamadas funciona, precisamos entender o que é uma estrutura de dados da pilha.
36+
37+
Uma estrutura de dados é um mecanismo de programação para organizar dados, para que possam ser usados com eficiência. Você já viu vários tipos de estruturas de dados, como matrizes e estruturas. Ambas as estruturas de dados fornecem mecanismos para armazenar dados e acessar esses dados de maneira eficiente. Existem muitas estruturas de dados adicionais que são comumente usadas em programação, algumas das quais são implementadas na biblioteca padrão e uma pilha é uma delas.
38+
39+
# Funcionamento de uma Pilha
40+
41+
- Olhe para o item superior da pilha(geralmente feito através de uma função chamada top(), mas às vezes chamada de peek())
42+
- Retire o item principal da pilha(feito por meio de uma função chamada pop())
43+
- Coloque um novo item no topo da pilha(feito através de uma função chamada push())
44+
45+
| Pilha | Saida |
46+
| ----- | ----- |
47+
| Stack | Vazia |
48+
| Push | 1 |
49+
| Stack | 1 |
50+
| Push | 2 |
51+
| Stack | 1 2 |
52+
| Push | 3 |
53+
| Stack | 1 2 3 |
54+
| Pop | |
55+
| Push | 1 2 |
56+
| Pop | |
57+
| Stack | 1 |
58+
59+
# O segmento da stack de chamadas
60+
61+
O segmento da pilha de chamadas mantém a memória usada para a pilha de chamadas. Quando o aplicativo é iniciado, a função main () é pressionada na pilha de chamadas pelo sistema operacional. Então o programa começa a executar.
62+
63+
Quando uma chamada de função é encontrada, a função é enviada para a pilha de chamadas. Quando a função atual termina, essa função é retirada da pilha de chamadas. Assim, observando as funções pressionadas na pilha de chamadas, podemos ver todas as funções que foram chamadas para chegar ao ponto de execução atual.
64+
65+
Nossa analogia da caixa de correio acima é bastante semelhante à maneira como a pilha de chamadas funciona. A própria pilha é um pedaço de tamanho fixo de endereços de memória. As caixas de correio são endereços de memória e os "itens" que estamos empurrando e popping na pilha são chamados de quadros de pilha. Um quadro de pilha controla todos os dados associados a uma chamada de função. Falaremos mais sobre os quadros de pilha em breve. O "marcador" é um registro (um pequeno pedaço de memória na CPU) conhecido como ponteiro da pilha (às vezes abreviado como "SP"). O ponteiro da pilha controla onde está atualmente a parte superior da pilha de chamadas.
66+
67+
A única diferença entre nossa pilha hipotética de caixa de correio e a pilha de chamadas é que, quando retiramos um item da pilha de chamadas, não precisamos apagar a memória (o equivalente a esvaziar a caixa de correio). Podemos apenas deixá-lo sobrescrito pelo próximo item enviado para esse pedaço de memória. Como o ponteiro da pilha estará abaixo desse local da memória, sabemos que o local da memória não está na pilha.
68+
69+
# A pilha de chamadas em ação
70+
71+
Vamos examinar mais detalhadamente como a pilha de chamadas funciona. Aqui está a sequência de etapas que ocorre quando uma função é chamada:
72+
73+
O programa encontra uma chamada de função.
74+
Um quadro de pilha é construído e empurrado na pilha. O quadro da pilha consiste em:
75+
O endereço da instrução além da chamada da função (chamado endereço de retorno). É assim que a CPU se lembra para onde retornar após a saída da função chamada.
76+
Todos os argumentos da função.
77+
Memória para quaisquer variáveis ​​locais.
78+
Cópias salvas de quaisquer registros modificados pela função que precisam ser restauradas quando a função retornar
79+
A CPU pula para o ponto inicial da função.
80+
As instruções dentro da função começam a ser executadas.
81+
Quando a função termina, ocorrem as seguintes etapas:
82+
83+
Os registros são restaurados da pilha de chamadas
84+
O quadro da pilha é retirado da pilha. Isso libera a memória para todas as variáveis ​​e argumentos locais.
85+
O valor de retorno é tratado.
86+
A CPU retoma a execução no endereço de retorno.
87+
Os valores de retorno podem ser manipulados de várias maneiras diferentes, dependendo da arquitetura do computador. Algumas arquiteturas incluem o valor de retorno como parte do quadro da pilha. Outros usam registradores de CPU.
88+
89+
Normalmente, não é importante conhecer todos os detalhes sobre como a pilha de chamadas funciona. No entanto, o entendimento de que as funções são efetivamente colocadas na pilha quando são chamadas e ativadas quando retornam fornece os fundamentos necessários para entender a recursão, além de alguns outros conceitos úteis na depuração.
90+
91+
Uma observação técnica: em algumas arquiteturas, a pilha de chamadas cresce para longe do endereço de memória 0. Em outras, cresce para o endereço de memória 0. Como conseqüência, os novos quadros de pilha enviados podem ter um endereço de memória maior ou menor que os anteriores.
92+
93+
Exemplo:
94+
95+
```cpp{0}
96+
int foo(int x)
97+
{
98+
// b
99+
return x;
100+
} // foo is popped off the call stack here
101+
102+
int main()
103+
{
104+
// a
105+
foo(5); // a função foo foi chamada no push() para a pilha
106+
// c
107+
108+
return 0;
109+
}
110+
```
111+
112+
Estouro de pilha(Não é piada com o site. juro.)
113+
114+
A pilha tem um tamanho limitado e, consequentemente, pode conter apenas uma quantidade limitada de informações. No Windows, o tamanho padrão da pilha é de 1 MB. Em algumas máquinas unix, pode ter até 8 MB. Se o programa tentar colocar muitas informações na pilha, resultará um excesso de pilha. O estouro de pilha acontece quando toda a memória da pilha foi alocada - nesse caso, alocações adicionais começam a transbordar para outras seções da memória.
115+
116+
O estouro de pilha geralmente é o resultado da alocação de muitas variáveis na pilha e / ou de muitas chamadas de funções aninhadas (onde a função A chama a função B chama a função C chama a função D etc ...) Nos sistemas operacionais modernos, o excesso da pilha geralmente faça com que seu sistema operacional emita uma violação de acesso e encerre o programa.
117+
118+
Aqui está um exemplo de programa que provavelmente causará um estouro de pilha. Você pode executá-lo em seu sistema e assisti-lo travar:
119+
120+
```cpp{0}
121+
#include <iostream>
122+
123+
int main()
124+
{
125+
int stack[10000000];
126+
std::cout << "hi";
127+
return 0;
128+
}
129+
```
130+
131+
A classe `std::vector` faz muito bem do uso da pilha, recomendado a se utilizar em vés do vetor normal, assim como a classe `std::array`

0 commit comments

Comments
 (0)