Escopo e tempo de vida
Esta aula aborda os conceitos de escopo e tempo de vida em C, explicando a diferença entre variáveis locais e globais, o uso de variáveis static e automáticas, e boas práticas para gerenciar a visibilidade e duração das variáveis.
Nesta aula, vamos explorar dois conceitos fundamentais em C: escopo e tempo de vida das variáveis. Escopo refere-se à região do programa onde uma variável pode ser acessada, enquanto tempo de vida (ou duração) determina por quanto tempo a variável existe na memória. Compreender esses conceitos é crucial para escrever código eficiente e evitar erros comuns, como variáveis não inicializadas ou conflitos de nomes.
Variáveis podem ser declaradas em diferentes contextos, como dentro de funções, fora de funções ou com modificadores específicos. Cada contexto define regras diferentes de visibilidade e persistência. Vamos detalhar cada um deles a seguir.
Local vs global
Uma variável local é declarada dentro de uma função ou bloco (delimitado por chaves). Ela só pode ser acessada dentro desse bloco e é criada quando o bloco é executado, sendo destruída ao final. Já uma variável global é declarada fora de qualquer função, geralmente no início do arquivo, e pode ser acessada por qualquer função do programa. Ela existe durante toda a execução do programa.
Exemplo de variável local: dentro da função main, declaramos int x = 10;. Essa variável só existe dentro de main. Exemplo de variável global: int global = 5; fora de qualquer função. Ela pode ser lida e modificada por qualquer função, o que pode levar a efeitos colaterais indesejados se não for usada com cuidado.
#include <stdio.h>
int global = 5; // variável global
void funcao() {
int local = 10; // variável local
printf("global na funcao: %d\n", global);
printf("local: %d\n", local);
}
int main() {
printf("global no main: %d\n", global);
funcao();
// printf("%d", local); // erro: local não é acessível aqui
return 0;
}No código acima, a variável global é visível tanto em main quanto em funcao. Já local só existe dentro de funcao. Tentar acessar local fora dela gera erro de compilação.
Variáveis static
Variáveis static podem ser usadas de duas formas: dentro de funções (static local) ou fora de funções (static global). Uma variável local static mantém seu valor entre chamadas da função, mas continua com escopo local. Já uma variável global static tem escopo restrito ao arquivo onde é declarada (não pode ser acessada por outros arquivos via extern).
Exemplo de static local: uma função que conta quantas vezes foi chamada.
#include <stdio.h>
void contador() {
static int count = 0; // inicializada apenas uma vez
count++;
printf("Chamada numero %d\n", count);
}
int main() {
contador(); // 1
contador(); // 2
contador(); // 3
return 0;
}A variável count é inicializada com 0 apenas na primeira chamada. Nas chamadas subsequentes, seu valor anterior é mantido. Se fosse uma variável local comum, ela seria reinicializada a cada chamada.
Exemplo de static global: limitar o acesso a uma variável a um único arquivo.
// arquivo: modulo.c
static int interno = 42; // só visível neste arquivo
void funcao() {
printf("%d\n", interno);
}Em outro arquivo, tentar acessar interno com extern causaria erro de ligação (linker).
Variáveis automáticas
Variáveis automáticas são aquelas declaradas dentro de um bloco (função ou bloco aninhado) sem o modificador static. Elas são criadas automaticamente ao entrar no bloco e destruídas ao sair. O termo "automática" vem do fato de que a alocação e desalocação são gerenciadas automaticamente pelo compilador (geralmente na pilha).
Na prática, todas as variáveis locais comuns são automáticas. O modificador auto é opcional e raramente usado, pois é o padrão.
#include <stdio.h>
void exemplo() {
auto int x = 5; // equivalente a int x = 5;
printf("%d\n", x);
}
int main() {
exemplo();
return 0;
}Variáveis automáticas têm tempo de vida limitado ao bloco. Por exemplo, não é seguro retornar um ponteiro para uma variável local automática, pois a memória será liberada após a função retornar.
int* perigoso() {
int valor = 10;
return &valor; // warning: retorna endereço de variável local
}
int main() {
int* p = perigoso();
printf("%d\n", *p); // comportamento indefinido
return 0;
}O código acima gera um aviso do compilador e o comportamento é indefinido, pois o ponteiro aponta para uma área que não é mais válida.
Boas práticas
1. Prefira variáveis locais a globais sempre que possível. Variáveis globais tornam o código menos modular e mais difícil de depurar, pois qualquer função pode alterá-las. Use globais apenas quando necessário (por exemplo, constantes compartilhadas).
2. Use static para encapsular funções e variáveis que não precisam ser expostas a outros arquivos. Isso reduz o acoplamento e evita conflitos de nomes.
3. Evite retornar ponteiros para variáveis locais automáticas. Se precisar que um valor persista, use alocação dinâmica (malloc) ou passe um ponteiro para uma variável externa.
4. Inicialize sempre as variáveis antes de usá-las, especialmente as automáticas, pois elas contêm lixo de memória se não forem inicializadas.
5. Para variáveis que precisam manter estado entre chamadas de função, considere usar static local em vez de uma variável global.
Referências
- C reference: Scope
- C reference: Storage duration
- GNU C Manual: Storage Class Specifiers
- POSIX: Storage Class
- Microsoft C: Storage Class
Exercícios
Escreva uma função que retorne o número de vezes que foi chamada, usando uma variável static local.
✓ Resposta:#include <stdio.h> int chamadas() { static int count = 0; count++; return count; } int main() { printf("%d\n", chamadas()); // 1 printf("%d\n", chamadas()); // 2 printf("%d\n", chamadas()); // 3 return 0; }Qual a diferença entre uma variável global static e uma variável global normal?
✓ Resposta: Uma variável global normal pode ser acessada por outros arquivos através deextern. Uma variável global static tem escopo restrito ao arquivo onde é declarada, não podendo ser acessada por outros arquivos.O que acontece se você tentar retornar um ponteiro para uma variável local automática?
✓ Resposta: O ponteiro se torna inválido (dangling pointer) e seu uso causa comportamento indefinido, pois a memória da variável local é liberada após a função retornar.Escreva um programa que demonstre o uso de uma variável automática em um bloco aninhado.
✓ Resposta:#include <stdio.h> int main() { int a = 1; { int a = 2; // variável automática local ao bloco printf("%d\n", a); // 2 } printf("%d\n", a); // 1 return 0; }Corrija o código abaixo para que a função retorne um valor seguro sem usar alocação dinâmica.
int* criaInt() { int x = 42; return &x; }✓ Resposta: Duas formas: retornar por valor (int) ou usar static local.// Retornando por valor int criaInt() { int x = 42; return x; } // Ou usando static int* criaInt() { static int x = 42; return &x; }