Em C, o escopo de uma variável define onde ela pode ser acessada no código, enquanto o tempo de vida determina por quanto tempo a variável existe na memória. Compreender esses conceitos é essencial para escrever programas previsíveis e evitar bugs sutis. Nesta aula, exploraremos os diferentes tipos de escopo e tempo de vida, desde variáveis locais até globais, passando por variáveis estáticas e automáticas, e discutiremos boas práticas para seu uso.

Local vs global

Variáveis locais são declaradas dentro de uma função ou bloco (delimitado por chaves). Elas só podem ser acessadas dentro desse bloco e têm tempo de vida limitado à execução do bloco. Por exemplo, uma variável declarada dentro de uma função é criada quando a função é chamada e destruída quando ela retorna.

Variáveis globais são declaradas fora de qualquer função, geralmente no topo do arquivo. Elas podem ser acessadas por qualquer função no mesmo arquivo (e, com declaração extern, em outros arquivos). Seu tempo de vida é toda a execução do programa: são alocadas na inicialização e liberadas apenas no término.

Exemplo:

#include <stdio.h>

int global = 10; // variável global

void funcao() {
    int local = 5; // variável local
    printf("global dentro da função: %d\n", global);
    printf("local: %d\n", local);
}

int main() {
    funcao();
    printf("global no main: %d\n", global);
    // printf("%d", local); // ERRO: local não é acessível aqui
    return 0;
}

Variáveis globais são úteis para dados compartilhados, mas devem ser usadas com moderação, pois podem criar dependências ocultas e dificultar a manutenção.

Variáveis static

Em C, a palavra-chave static modifica o tempo de vida e/ou o escopo de uma variável. Quando aplicada a uma variável local, ela faz com que a variável mantenha seu valor entre chamadas da função, mas ainda só é acessível dentro da função. O tempo de vida passa a ser o da execução do programa, como uma variável global, mas o escopo permanece local.

Exemplo:

#include <stdio.h>

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

int main() {
    contador(); // 1
    contador(); // 2
    contador(); // 3
    return 0;
}

Variáveis estáticas globais (declaradas fora de funções com static) têm escopo limitado ao arquivo onde são declaradas, ou seja, não podem ser acessadas por outros arquivos mesmo com extern. Isso é útil para encapsulamento.

Variáveis automáticas

O termo "automática" refere-se à classe de armazenamento padrão para variáveis locais: auto. Na prática, a palavra-chave auto raramente é usada, pois é o padrão. Variáveis automáticas são alocadas na pilha (stack) e têm tempo de vida limitado ao bloco onde são declaradas. Elas são criadas quando o bloco é executado e destruídas ao final.

Exemplo:

#include <stdio.h>

void exemplo() {
    auto int x = 10; // equivalente a 'int x = 10'
    printf("%d\n", x);
} // x é destruído aqui

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

Variáveis automáticas não são inicializadas automaticamente; conterão lixo se não forem explicitamente inicializadas. Por isso, é boa prática sempre inicializar variáveis locais.

Boas práticas

1. Prefira variáveis locais a globais sempre que possível. Elas tornam o código mais modular e facilitam a compreensão do fluxo de dados.

2. Use static para funções e variáveis que não precisam ser visíveis fora do arquivo atual (encapsulamento).

3. Evite modificar variáveis globais dentro de funções, a menos que seja estritamente necessário. Isso pode causar efeitos colaterais inesperados.

4. Sempre inicialize variáveis automáticas antes de usá-las para evitar comportamento indefinido.

5. Use nomes descritivos para variáveis globais e estáticas, e considere prefixos (como g_ para globais) para diferenciá-las.

6. Lembre-se de que variáveis estáticas locais mantêm seu valor entre chamadas, mas podem dificultar a reentrância de funções. Use-as com cuidado.

Referências

Exercícios

  1. O que será impresso pelo seguinte código? Explique.

    #include <stdio.h>
    
    int x = 1;
    
    void func() {
        int x = 2;
        printf("%d ", x);
    }
    
    int main() {
        func();
        printf("%d\n", x);
        return 0;
    }

    ✓ Resposta: O código imprime "2 1". Dentro de func, a variável local x (escopo local) esconde a global x, então x vale 2. No main, a variável global x é acessada, valendo 1.
  2. Escreva uma função que retorne o número de vezes que foi chamada, usando uma variável estática.

    ✓ Resposta:
    int contador() {
        static int count = 0;
        return ++count;
    }
  3. Qual a diferença entre uma variável global e uma variável estática global?

    ✓ Resposta: Uma variável global (sem static) pode ser acessada por outros arquivos via declaração extern. Uma variável estática global tem escopo restrito ao arquivo onde é declarada, não podendo ser acessada por outros arquivos.
  4. O que acontece se você tentar acessar uma variável local fora do seu bloco? Dê um exemplo.

    ✓ Resposta: O compilador gera um erro de compilação (variável não declarada). Exemplo:
    void func() {
        int x = 10;
    }
    int main() {
        printf("%d", x); // erro: 'x' undeclared
        return 0;
    }
  5. Considere o código abaixo. Quais variáveis são automáticas? Quais têm tempo de vida estático?

    #include <stdio.h>
    
    int a = 1;
    static int b = 2;
    
    void func() {
        int c = 3;
        static int d = 4;
        printf("%d %d %d %d\n", a, b, c, d);
    }
    
    int main() {
        func();
        return 0;
    }

    ✓ Resposta: Variáveis automáticas: c (local dentro de func). Variáveis com tempo de vida estático: a (global), b (estática global), d (estática local).