O que você vai aprender
Descrever o registro de ativação de uma função e seus campos.
Explicar a pilha de execução em chamadas e na recursão.
Distinguir alocação na pilha e no heap e seus riscos.
Relacionar a organização de memória às decisões do compilador.
Onde o programa "mora" enquanto roda
O compilador traduziu o código — mas para onde vão as variáveis locais, os parâmetros, o endereço de retorno quando o programa executa? O ambiente de execução (runtime) é a organização de memória que o compilador planeja para que tudo funcione.
Entender a pilha de execução explica recursão, estouro de pilha, vazamentos de memória e por que variáveis locais "somem" quando a função retorna.
A memória de um processo
Um programa em execução divide sua memória em regiões:
instruções→Dados
globais→Heap
cresce ↑→Pilha
cresce ↓
Pilha e heap crescem em sentidos opostos no espaço livre. O compilador organiza, sobretudo, a pilha — onde vivem as chamadas de função.
O registro de ativação
É a "ficha" daquela execução específica da função. Cada chamada tem o seu próprio registro, independente das demais.
O que cada campo guarda
Os campos típicos de um registro de ativação:
- Parâmetros: os argumentos passados na chamada.
- Variáveis locais: as declaradas dentro da função.
- Endereço de retorno: onde continuar a execução após a função terminar.
- Valor de retorno: espaço para devolver o resultado ao chamador.
- Elo de controle: ponteiro para o registro do chamador (frame anterior).
- Elo de acesso: ponteiro para o escopo léxico envolvente (em linguagens com funções aninhadas).
Chamadas empilhando frames
A função recursiva fat(3) gera uma cadeia de chamadas, cada uma com seu frame:
O caso base fat(1) retorna primeiro; depois fat(2), depois fat(3) — a ordem inversa do empilhamento.
Evolução da pilha passo a passo
A pilha de execução
O ponteiro de pilha (stack pointer) marca o topo. Empilhar e desempilhar são apenas ajustes desse ponteiro — operações baratíssimas, por isso a pilha é tão eficiente.
Por que a recursão funciona
Cada chamada recursiva cria um frame independente, com sua própria cópia dos parâmetros e locais. Por isso fat(3) e fat(2) coexistem sem confundir seus valores de n.
A pilha de pratos
Pilha × heap
| Pilha (stack) | Heap | |
|---|---|---|
| O que guarda | Locais, parâmetros, retorno | Memória dinâmica (new/malloc) |
| Tempo de vida | Da chamada ao retorno | Até a liberação/coleta |
| Gerência | Automática (LIFO) | Manual ou coletor de lixo |
| Velocidade | Muito rápida (move ponteiro) | Mais lenta (busca espaço livre) |
| Risco típico | Stack overflow | Vazamento / fragmentação |
O protocolo de chamada
empilha frame→Executa
usa locais→Retorna
valor + endereço→Desempilha
frame some
A divisão de tarefas entre chamador e chamado (quem empilha argumentos, quem limpa a pilha) é a convenção de chamada — fixada pelo compilador e a arquitetura.
Convenção de chamada e ponteiro de quadro
Enquanto o stack pointer se move conforme se empilha/desempilha, o frame pointer fica estável durante a execução da função, dando endereços previsíveis: local_x = frame_pointer - 8, por exemplo. É na tabela de símbolos (aula 9) que esses deslocamentos foram calculados.
Verifique seu entendimento
Por que cada chamada recursiva não atrapalha as outras?
Local na pilha × objeto no heap
Em uma linguagem como C, compare:
Retornar um ponteiro para v seria um erro grave: a memória já não existe após o retorno.
Perigos do ambiente de execução
Vazamento de memória. Alocar no heap e nunca liberar faz o consumo crescer indefinidamente.
Ponteiro pendurado (dangling). Usar memória da pilha após o retorno da função, ou do heap após o free.
Confundir pilha e heap. Achar que variáveis locais sobrevivem à função, ou que o heap se limpa sozinho sem coletor.
Convivendo bem com a memória
Para cada alocação no heap, planeje a liberação (ou confie no coletor de lixo, se houver).
Nunca retorne ponteiro para variável local: ela morre com o frame.
Lembre que a pilha é rápida: prefira-a para dados de vida curta e tamanho conhecido.
Pense antes de revelar
O que aconteceria ao chamar fat(100000) numa recursão sem otimização de cauda?
Revise os termos
Como esta aula se liga ao curso
- Usa a aula 9 (tabela de símbolos): os deslocamentos das variáveis no frame foram calculados ali.
- Prepara a aula 12 (código): a geração de código emite as instruções que empilham/desempilham frames.
- Liga-se à RI (aula 10): chamadas de função no TAC viram protocolos de pilha no código final.
- Explica fenômenos práticos: stack overflow e vazamentos que o programador encontra no dia a dia.
O essencial da aula
Atividade em grupo · Desenhando a pilha
Em duplas, desenhem a evolução da pilha em uma função recursiva.
Roteiro
- Tomem fatorial(4) ou fibonacci(4).
- Desenhem a pilha de registros a cada chamada e a cada retorno.
- Marquem o valor retornado por cada frame.
- Discutam: o que aconteceria com a entrada 100000?
Mini-quiz · Aula 11
20 questões sobre esta aula. Escolha e veja a explicação na hora.
📌 Resumo — leve isto para a prova
- Cada chamada cria um registro de ativação na pilha (parâmetros, locais, retorno, elos).
- A pilha é LIFO; a recursão usa um frame independente por chamada.
- A pilha guarda dados de vida curta (automática); o heap guarda memória dinâmica (manual/coletada).
- Estouro de pilha e vazamentos de memória são os riscos clássicos do runtime.
- O frame pointer dá acesso estável a locais e parâmetros por deslocamento.