As classes de armazenamento em C definem o escopo, o tempo de vida e, em alguns casos, o local de armazenamento de variáveis e funções. Elas são fundamentais para o controle fino de como os dados são mantidos na memória e como diferentes partes do programa podem acessá-los. Nesta aula, vamos explorar as quatro classes principais: auto, register, static e extern, entendendo suas diferenças e aplicações práticas.

Dominar essas classes é essencial para escrever código eficiente e evitar bugs sutis relacionados a variáveis globais, locais e à comunicação entre arquivos. Vamos começar com as classes mais simples e avançar para as mais complexas, sempre com exemplos claros.

auto, register

A classe auto é a padrão para variáveis locais. Ela indica que a variável é automática, ou seja, é criada na entrada do bloco em que é declarada e destruída na saída. Na prática, raramente escrevemos auto porque é o comportamento implícito de qualquer variável local não estática.

Já a classe register sugere ao compilador que a variável seja armazenada em um registrador do processador, para acesso mais rápido. No entanto, o compilador pode ignorar essa sugestão, e não é possível obter o endereço de uma variável register (com o operador &). O uso de register é raro hoje em dia, pois os compiladores otimizam bem o código automaticamente.

#include <stdio.h>

int main() {
    // auto é implícito, podemos omitir
    auto int x = 10;  // equivalente a int x = 10;

    // register: sugestão para armazenar em registrador
    register int y = 20;

    printf("x = %d, y = %d\n", x, y);

    // Não é permitido: register int *p = &y; // erro!

    return 0;
}

No exemplo, x é automaticamente auto e y é register. A tentativa de obter o endereço de y resultaria em erro de compilação.

static

A classe static tem dois usos principais: em variáveis locais e em variáveis/ funções globais. Quando aplicada a uma variável local, ela altera o tempo de vida: a variável não é destruída ao sair do bloco, mas mantém seu valor entre chamadas da função. O escopo continua sendo o bloco, mas a variável é inicializada apenas uma vez.

Quando aplicada a uma variável global ou função, static restringe a visibilidade ao arquivo onde é declarada, impedindo que outros arquivos a acessem via extern. Isso é útil para encapsulamento.

#include <stdio.h>

void contador() {
    static int count = 0; // inicializada apenas uma vez
    count++;
    printf("chamada %d\n", count);
}

static int interno = 100; // visível apenas neste arquivo

int main() {
    contador(); // chamada 1
    contador(); // chamada 2
    contador(); // chamada 3

    printf("interno = %d\n", interno);
    return 0;
}

No código, a variável count mantém seu valor entre chamadas, porque é static. Sem static, ela seria reinicializada a cada chamada.

extern

A classe extern é usada para declarar uma variável ou função que é definida em outro arquivo ou em outro ponto do código. Ela não aloca memória; apenas informa ao compilador que a variável existe em algum lugar. Isso permite que múltiplos arquivos compartilhem variáveis globais.

É importante diferenciar declaração (com extern) de definição (sem extern). A definição aloca memória e pode ser feita apenas uma vez em todo o programa.

// arquivo1.c
#include <stdio.h>

int global = 42; // definição

void funcao();

int main() {
    printf("global em main: %d\n", global);
    funcao();
    return 0;
}

// arquivo2.c
extern int global; // declaração, não definição

void funcao() {
    printf("global em funcao: %d\n", global);
}

Neste exemplo, a variável global é definida em arquivo1.c e usada em arquivo2.c via extern. Se tentássemos definir global novamente em arquivo2.c, haveria erro de linkagem.

Tempo de vida e visibilidade

O tempo de vida de uma variável é o período durante o qual ela existe na memória. Variáveis auto e register têm tempo de vida automático (do início ao fim do bloco). Variáveis static locais têm tempo de vida estático (duram todo o programa, mas só são acessíveis no bloco). Variáveis globais (incluindo extern) também têm tempo de vida estático.

A visibilidade (ou escopo) determina onde a variável pode ser referenciada. Variáveis locais são visíveis apenas no bloco onde foram declaradas. Variáveis globais são visíveis em todo o arquivo a partir do ponto de declaração, e em outros arquivos se declaradas com extern. static global restringe a visibilidade ao arquivo.

Resumindo em uma tabela:

ClasseEscopoTempo de vidaLocalização
autoBlocoAutomáticoMemória (pilha)
registerBlocoAutomáticoRegistrador (sugestão)
static (local)BlocoEstáticoMemória (área estática)
static (global)ArquivoEstáticoMemória (área estática)
externArquivo (ou outros)EstáticoMemória (definido em outro lugar)

Boas práticas

  • Evite variáveis globais sempre que possível; prefira passar parâmetros.
  • Use static para funções que não precisam ser visíveis fora do arquivo.
  • Use extern apenas para compartilhar variáveis entre arquivos, e sempre com moderação.
  • Não use register em código moderno; os compiladores otimizam melhor.
  • Inicialize sempre variáveis estáticas para evitar valores indefinidos.

Exercícios

  1. Explique a diferença entre tempo de vida automático e estático, dando um exemplo de cada.
  2. Escreva um programa que use uma variável static local para contar quantas vezes uma função foi chamada.
  3. O que acontece se você tentar obter o endereço de uma variável declarada como register? Justifique.
  4. Considere dois arquivos: a.c define int x = 5; e b.c declara extern int x;. Se b.c tentar modificar x, qual será o efeito? Explique.
  5. Escreva um programa que demonstre o uso de static em uma variável global para limitar o acesso a outro arquivo.

✓ Resposta: Tempo de vida automático: a variável é criada na entrada do bloco e destruída na saída (ex.: variável local comum). Tempo de vida estático: a variável é criada uma vez e dura até o fim do programa (ex.: variável global ou static local).

✓ Resposta:
#include <stdio.h>

void func() {
    static int count = 0;
    count++;
    printf("Chamada %d\n", count);
}

int main() {
    func();
    func();
    func();
    return 0;
}

✓ Resposta: O compilador gera um erro, porque variáveis register não têm endereço de memória (podem estar em registradores). O padrão C proíbe o uso do operador & em tais variáveis.

✓ Resposta: A variável x é a mesma (compartilhada). Modificá-la em b.c altera o valor para ambos os arquivos, pois extern não cria uma nova variável, apenas referencia a existente.

✓ Resposta:
// arquivo1.c
static int segredo = 42;

int main() {
    printf("%d\n", segredo);
    return 0;
}

// arquivo2.c (não pode acessar segredo, pois é static)
// extern int segredo; // erro de linkagem, porque segredo não é visível fora de arquivo1.c

Referências