Aula 15 — Escopo de Variáveis

Local, global, de bloco e static — onde uma variável existe e por quanto tempo

1. O que é Escopo?

Toda variável em C tem um escopo: a região do código onde ela existe e pode ser acessada. Fora desse escopo, a variável simplesmente não existe — tentar usá-la gera erro de compilação.

Escopo é a região do código-fonte onde um identificador (variável, função, constante) é visível e pode ser utilizado. Em C, o escopo é determinado principalmente pelo local da declaração em relação às chaves { } e pela posição no arquivo.

C reconhece quatro categorias de escopo:

Tipo de escopo Onde é declarado Visível de Dura até
De bloco (local) Dentro de { } Ponto da declaração Fim do bloco }
De parâmetro Lista de parâmetros da função Início do corpo da função Fim da função }
De arquivo (global) Fora de qualquer função Ponto da declaração até fim do arquivo Fim do programa
Local static Dentro de função, com static Apenas dentro da função Fim do programa (persiste entre chamadas)

2. Variáveis Locais (Escopo de Bloco)

Uma variável declarada dentro de um par de chaves { } é local àquele bloco. Ela nasce quando o programa entra no bloco e morre quando sai.

#include <stdio.h>

void exemploLocal() {
    int x = 10;                  // x existe apenas dentro de exemploLocal

    if (x > 5) {
        int y = 20;              // y existe apenas dentro deste if
        printf("x=%d y=%d\n", x, y);   // OK: x e y visíveis aqui
    }

    // printf("%d", y);          // ❌ ERRO: y não existe mais aqui
    printf("x=%d\n", x);        // OK: x ainda existe aqui
}                                // x é destruída ao sair de exemploLocal

int main() {
    exemploLocal();
    // printf("%d", x);          // ❌ ERRO: x não existe em main
    return 0;
}
x=10 y=20
x=10

Variável de controle do for (C99)

Declarar a variável de controle dentro do próprio for limita seu escopo ao bloco do loop — uma boa prática:

for (int i = 0; i < 5; i++) {
    printf("%d ", i);     // OK: i existe dentro do for
}
// printf("%d", i);       // ❌ ERRO: i não existe fora do for (C99)
⚠️ Variáveis locais não são inicializadas automaticamente. Em C, variáveis locais contêm o que estiver na memória naquele momento — chamado de lixo de memória. Ler uma variável local antes de atribuir um valor é comportamento indefinido.
int x;                   // ❌ contém lixo — valor imprevisível
printf("%d\n", x);       // comportamento indefinido!

int y = 0;               // ✅ sempre inicialize antes de usar
Boas práticas: Declare variáveis no escopo mais restrito possível — o mais próximo do ponto de uso. Isso reduz conflitos de nomes, torna o código mais claro e ajuda o compilador a detectar erros.

3. Variáveis Globais (Escopo de Arquivo)

Uma variável declarada fora de qualquer função — normalmente no topo do arquivo — é global. Ela é visível em todas as funções do arquivo e existe durante toda a execução do programa.

#include <stdio.h>

int contador = 0;           // global — visível em todas as funções
const float TAXA = 0.05f;   // constante global — uso adequado

void incrementar() {
    contador++;             // acessa e modifica a global diretamente
}

void exibir() {
    printf("Contador: %d | Taxa: %.0f%%\n", contador, TAXA * 100);
}

int main() {
    exibir();           // Contador: 0 | Taxa: 5%
    incrementar();
    incrementar();
    incrementar();
    exibir();           // Contador: 3 | Taxa: 5%
    return 0;
}
Contador: 0 | Taxa: 5%
Contador: 3 | Taxa: 5%

Variáveis globais são inicializadas automaticamente com zero

Ao contrário das locais, variáveis globais são sempre zeradas antes do programa começar:

Tipo Valor padrão (global)
int0
float / double0.0
char'\0'
PonteiroNULL

4. Sombreamento (Shadowing) — Local Esconde Global

Quando uma variável local tem o mesmo nome de uma variável global, a local sombreia (esconde) a global dentro do seu escopo. A global continua existindo, mas fica temporariamente inacessível pelo nome.

#include <stdio.h>

int valor = 100;            // global

void demonstrar() {
    printf("global: %d\n", valor);   // 100 — ainda sem sombra

    int valor = 50;                  // local — cria sombra sobre a global
    printf("local:  %d\n", valor);   // 50

    {
        int valor = 25;              // bloco interno — sombra da sombra
        printf("bloco:  %d\n", valor); // 25
    }

    printf("local:  %d\n", valor);   // 50 — volta ao escopo local
}

int main() {
    demonstrar();
    printf("global: %d\n", valor);   // 100 — global intocada
    return 0;
}
global: 100
local: 50
bloco: 25
local: 50
global: 100
⚠️ Sombreamento é uma fonte clássica de bugs. Use gcc -Wall -Wshadow para receber avisos do compilador sempre que houver sombreamento no seu código.

5. Quando Usar (e Quando Evitar) Variáveis Globais

✅ Quando globais fazem sentido ❌ Quando globais causam problemas
Constantes imutáveis (const) compartilhadas por todo o programa Variáveis que podem ser modificadas por qualquer função
Contadores ou flags de estado do programa inteiro Variáveis com nomes genéricos (i, temp, aux)
Tabelas de configuração apenas de leitura Variáveis usadas para evitar passar parâmetros
Programas pequenos e de arquivo único Projetos com múltiplos arquivos e equipes
❌ Anti-padrão: usar global para "facilitar" o retorno
int resultado;              // global desnecessária
void somar(int a, int b) {
    resultado = a + b;      // qualquer função pode corromper isso
}
✅ Correto: retornar o valor pela assinatura
int somar(int a, int b) {
    return a + b;           // limpo, sem efeitos colaterais
}
Princípio da encapsulação: Uma função deve depender apenas dos seus parâmetros e variáveis locais. Funções que seguem esse princípio são chamadas de funções puras — muito mais fáceis de testar, reutilizar e depurar.

6. Variável Local static — Persistindo Entre Chamadas

C tem um tipo especial de variável local: a variável local static. Ela tem escopo local (só visível dentro da função) mas duração global — não é destruída ao retornar e persiste entre chamadas.

Uma variável local declarada com static é inicializada uma única vez (na primeira execução da função) e mantém seu valor entre chamadas consecutivas — como uma "memória privada" daquela função.
#include <stdio.h>

void contarChamadas() {
    static int n = 0;       // inicializada UMA vez; persiste entre chamadas
    n++;
    printf("Esta função foi chamada %d vez(es)\n", n);
}

int main() {
    contarChamadas();   // Esta função foi chamada 1 vez(es)
    contarChamadas();   // Esta função foi chamada 2 vez(es)
    contarChamadas();   // Esta função foi chamada 3 vez(es)
    return 0;
}
Esta função foi chamada 1 vez(es)
Esta função foi chamada 2 vez(es)
Esta função foi chamada 3 vez(es)

Comparação: local normal vs local static vs global

Característica Local normal Local static Global
Visibilidade Apenas dentro da função Apenas dentro da função Todo o arquivo
Inicialização automática ❌ Contém lixo ✅ Zero (uma vez) ✅ Zero
Persiste entre chamadas? ❌ Destruída ao retornar ✅ Sim ✅ Sim
Armazenamento Stack (pilha) Segmento de dados Segmento de dados

Exemplo prático: gerador de ID único

#include <stdio.h>

int gerarID() {
    static int proximoID = 1000;   // começa em 1000, nunca reinicia
    return proximoID++;
}

int main() {
    printf("ID: %d\n", gerarID());   // ID: 1000
    printf("ID: %d\n", gerarID());   // ID: 1001
    printf("ID: %d\n", gerarID());   // ID: 1002
    return 0;
}
ID: 1000
ID: 1001
ID: 1002
Uso clássico de static local: Geradores de IDs únicos, contadores de chamadas, buffers de cache — tudo isso sem expor a variável como global.

7. Escopo de Parâmetros

Os parâmetros de uma função têm escopo equivalente ao de variáveis locais declaradas no início do corpo. Eles existem enquanto a função está executando e são destruídos ao retornar.

#include <stdio.h>

void processar(int valor, float taxa) {
    // 'valor' e 'taxa' existem aqui — escopo idêntico ao de locais
    valor = valor + (int)(valor * taxa);   // modifica a cópia local
    printf("Valor processado: %d\n", valor);
}   // 'valor' e 'taxa' são destruídos aqui

int main() {
    int preco = 100;
    processar(preco, 0.1f);
    printf("Preço original: %d\n", preco);   // 100 — intocado
    return 0;
}
Valor processado: 110
Preço original: 100
Parâmetros são sempre recebidos por valor em C (cópia). Para modificar a variável original, é necessário usar ponteiros — como visto na Aula 14 (Call by Reference).

8. Exemplo Completo — Todos os Escopos em Um Programa

#include <stdio.h>

/* ── ESCOPO GLOBAL ─────────────────────────────── */
int totalVendas = 0;                   // global: visível em todo o arquivo
const float IMPOSTO = 0.12f;           // constante global: uso adequado

float calcularPrecoFinal(float preco, int quantidade) {
    /* ── ESCOPO DE PARÂMETRO ────────────────── */
    // 'preco' e 'quantidade' existem apenas aqui

    /* ── ESCOPO LOCAL (bloco da função) ────── */
    float subtotal = preco * quantidade;
    float imposto  = subtotal * IMPOSTO;   // usa constante global
    float total    = subtotal + imposto;

    /* ── STATIC LOCAL ───────────────────────── */
    static int numTransacoes = 0;          // persiste entre chamadas
    numTransacoes++;

    /* ── ESCOPO DE BLOCO (if interno) ───────── */
    if (total > 500.0f) {
        float desconto = total * 0.05f;    // existe apenas neste if
        total -= desconto;
        printf("  Desconto de 5%% aplicado: -R$%.2f\n", desconto);
    }   // 'desconto' destruída aqui

    totalVendas++;                         // modifica global
    printf("  Transacao #%d | Total: R$%.2f\n", numTransacoes, total);
    return total;
}

int main() {
    printf("=== Caixa da Loja ===\n");
    calcularPrecoFinal(80.0f,  3);   // 3 x R$80  = R$240 + imposto
    calcularPrecoFinal(200.0f, 4);   // 4 x R$200 = R$800 -> desconto!
    calcularPrecoFinal(50.0f,  2);   // 2 x R$50  = R$100 + imposto
    printf("\nTotal de vendas no sistema: %d\n", totalVendas);
    return 0;
}
=== Caixa da Loja ===
  Transacao #1 | Total: R$268.80
  Desconto de 5% aplicado: -R$44.80
  Transacao #2 | Total: R$851.20
  Transacao #3 | Total: R$112.00

Total de vendas no sistema: 3

9. C vs. Java — Escopo de Variáveis

Aspecto C Java
Local não inicializada ⚠️ Contém lixo — comportamento indefinido ✅ Erro de compilação se usada sem inicializar
Variável global Declarada fora de qualquer função Não existe — substituída por atributos static de classe
Inicialização automática Apenas globais e static locais (com zero) Atributos de objetos são zerados automaticamente
Variável de controle do for Morre fora do for (C99) Morre fora do for — igual ao C
Sombreamento Permitido (com aviso -Wshadow) Permitido para locais, mas não para atributos de instância
Local static ✅ Existe — persiste entre chamadas ❌ Não existe — sem equivalente direto

Side-by-side: variável global em C vs atributo estático em Java

C — variável global
int contador = 0;

void incrementar() {
    contador++;
}

int obter() {
    return contador;
}
Java — atributo estático
class Contador {
    static int valor = 0;

    static void incrementar() {
        valor++;
    }

    static int obter() {
        return valor;
    }
}

Quiz — Aula 15

1. Qual é a saída do código abaixo?

#include <stdio.h>
int x = 5;
void f() { int x = 10; printf("%d ", x); }
int main() { f(); printf("%d\n", x); }

2. Qual é a saída?

#include <stdio.h>
void contar() {
    static int n = 0;
    n++;
    printf("%d ", n);
}
int main() { contar(); contar(); contar(); }

3. O que acontece ao tentar usar uma variável local depois do bloco em que foi declarada?

4. Diferentemente das variáveis locais, variáveis globais em C são inicializadas automaticamente. Qual é o valor inicial de um float global não inicializado explicitamente?

5. Qual a principal diferença entre uma variável local static e uma variável global?

6. Qual é a saída?

#include <stdio.h>
int v = 1;
void f() {
    int v = 2;
    { int v = 3; printf("%d ", v); }
    printf("%d ", v);
}
int main() { f(); printf("%d\n", v); }

7. Por que usar variáveis globais modificáveis indiscriminadamente é uma má prática?

8. Em Java, usar uma variável local sem inicializá-la gera erro de compilação. Em C, o mesmo código: