Alocação de memória

No livro "Understanding and Using C Pointers", Richard Reese enfatiza que um programa escrito na linguagem de programação C lida com três tipos de memória: estática (global), automática e dinâmica.

Para entender melhor a organização da memória, vamos fazer uso de um esquema visual, que representa a memória em diferentes compartimentos: stack, heap, uninitialized data, initialized data e text.

Layout de memória em C

♾️ Alocação estática

À memória estática (ou global), cabe o armazenamento das variáveis globais e das variáveis estáticas. Tais variáveis são alocadas na memória quando o programa é iniciado - e persistem na memória enquanto o programa estiver em execução.

Aqui, é importante relembrar a diferença entre variáveis globais e variáveis estáticas. Enquanto as variáveis globais são declaradas fora do método main() e são acessíveis por todas as funções, as variáveis estáticas têm seu escopo limitado às funções em que foram declaradas.

Variável estática

#include<stdio.h>
#include<stdlib.h>

void funcao(){
    static int valor = 10;
    valor++;
    printf("Valor: %d\n", valor);
}

int main(){
    funcao();
    funcao();
    // printf("Valor: %d\n", valor); // linha incorreta
}

No código acima, observe que há uma linha comentada. Essa linha está aí para evidenciar que a variável valor não é acessível na função main(), sendo limitada ao escopo da função funcao().

Variável global

#include<stdio.h>
#include<stdlib.h>

int valor = 10;
    
void funcao(){
    valor++;
    printf("Valor: %d\n", valor);
}

int main(){
    funcao();
    funcao();
    printf("Valor: %d\n", valor); // linha incorreta
}

As variáveis estáticas e globais, quando ainda não inicializadas, são armazenadas em uma região de memória chamada "Unininitialized Data" (BSS). Quando já inicializadas, são armazenadas em outra região, chamada de "Initialized Data". Em alguns esquemas visuais, essas duas regiões são unificadas com o nome de DATA.

🤖 Alocação automática

Na chamada memória automática, são alocadas as variáveis criadas em funções. Como o escopo dessas variáveis é limitado às funções, tais variáveis permanecem na memória apenas enquanto as funções estiverem em execução. Essa região de memória é chamada de stack.

No exemplo abaixo, todas as variáveis apresentadas são armazenadas na região stack:
#include<stdio.h>
#include<stdlib.h>
   
int ehPar(int valor){
    if(valor % 2 == 0) return 1;
    return 0;
}

int main(){
    int numero = 14;
    int resposta = ehPar(valor);
    printf("Verificação: %d\n", resposta);
}

Isso acontece neste fragmento de código porque todas as variáveis têm seu escopo limitado às funções, não existindo variáveis globais ou estáticas.

Acompanhe a seguir o esquema visual, que ilustra o empilhamento das funções na stack, evidenciando a passagem por cópia e o tempo de vida das varáveis.

Ilustração do empilhamento de funções na stack de memória.

📟 Alocação dinâmica

Uma das limitações que conhecemos quando aprendermos a programar é o fato de que algumas estruturas possuem tamanho fixo. Ao declararmos um vetor, por exemplo, precisamos especificar quantos elementos ele deve armazenar.

Para superar essa limitação, podemos fazer o uso de alocação dinâmica de memória (recursos que várias linguagens de programação já oferecem de forma simples). Nesse caso, as variáveis são alocadas em tempo de execução em uma região de memória chamada heap.

Ao mesmo tempo em que traz maior flexibilidade ao desenvolvimento de software, a alocação dinâmica de memória pode exigir maior atenção dos desenvolvedores. Na linguagem C, por exemplo, cabe ao desenvolver a tarefa de desalocar a memória quando necessário. Algumas linguagens, como o Java, executam esse processo de forma automática (mas também limitam as ações do programador).

🎬 Vídeo de apoio

📖 Bibliografia

Livros

  • SEBESTA, Robert W. Conceitos de linguagens de programação. 11. ed. Porto Alegre: Bookman, 2018. xvi, 757 p. ISBN 9788582604687.

Artigos