Valores fixos no programa — macros de pré-processador versus variáveis somente leitura
Uma constante é um valor que não deve ser alterado durante a execução do programa. Em vez de espalhar números e textos literais pelo código — prática chamada de magic numbers — damos um nome significativo a esses valores fixos. Isso torna o código mais legível, seguro e fácil de manter.
#define, que cria uma macro substituída antes da compilação, e a palavra-chave const, que declara uma variável somente leitura com tipo definido.
area = 3.14159 * r * r; versus area = PI * r * r;. O segundo é imediatamente compreensível. Se o valor de PI precisar ser atualizado para mais casas decimais, basta mudar uma linha — a definição da constante — em vez de buscar e substituir todos os 3.14159 espalhados pelo código.
A diretiva #define instrui o pré-processador — uma etapa que ocorre antes da compilação propriamente dita — a substituir textualmente cada ocorrência do nome definido pelo seu valor correspondente.
#define NOME valor
#include <stdio.h>
#define PI 3.14159
#define GRAVIDADE 9.81
#define NOME "Curso de C"
#define MAXIMO 100
int main() {
printf("PI = %.5f\n", PI);
printf("Gravidade = %.2f m/s²\n", GRAVIDADE);
printf("Bem-vindo ao %s!\n", NOME);
printf("Valor máximo: %d\n", MAXIMO);
return 0;
}
PI = 3.14159
Gravidade = 9.81 m/s²
Bem-vindo ao Curso de C!
Valor máximo: 100
Antes de compilar, o pré-processador lê o código e substitui mecanicamente cada ocorrência do nome pelo valor — como um "localizar e substituir" automático. O compilador nunca vê o nome PI; ele vê diretamente o número 3.14159:
| Código que você escreve | Código que o compilador recebe |
|---|---|
area = PI * r * r; |
area = 3.14159 * r * r; |
if (x > MAXIMO) |
if (x > 100) |
#define TAXA "dez" onde se espera um número não são detectados durante a definição da macro — o erro só aparece quando a macro é usada. Por isso o const é geralmente preferido quando a segurança de tipos é importante.
O #define também pode definir expressões inteiras. Nesse caso, use sempre parênteses para evitar problemas de precedência na substituição textual:
// SEM parênteses — perigoso! #define DOBRO_ERRADO(x) x * 2 // COM parênteses — seguro #define DOBRO(x) ((x) * 2) #define QUADRADO(x) ((x) * (x)) int main() { printf("%d\n", DOBRO(5)); // 10 printf("%d\n", DOBRO(3 + 1)); // 8 (com parênteses: (3+1)*2) printf("%d\n", DOBRO_ERRADO(3+1)); // 5 ERRADO! expande para: 3+1*2 = 5 printf("%d\n", QUADRADO(4)); // 16 return 0; }
10
8
5
16
O qualificador const declara uma variável cujo valor não pode ser modificado após a inicialização. Ao contrário do #define, uma variável const tem tipo definido, ocupa espaço na memória e é verificada pelo compilador.
const tipo nome = valor;const sem valor e atribuí-lo depois.
#include <stdio.h>
int main() {
const float PI = 3.14159f;
const double EULER = 2.718281828;
const int ANO_BASE = 2024;
const char GRAU = 'C'; // grau Celsius
float raio = 5.0;
float area = PI * raio * raio;
printf("Área do círculo: %.2f\n", area);
printf("e (Euler): %.9f\n", EULER);
printf("Ano base: %d\n", ANO_BASE);
printf("Unidade: °%c\n", GRAU);
// PI = 3.0; ← erro de compilação: assignment of read-only variable
return 0;
}
Área do círculo: 78.54
e (Euler): 2.718281828
Ano base: 2024
Unidade: °C
const no momento da declaração. Tentativas de modificar uma variável const geram erro de compilação — que é exatamente o comportamento desejado. O compilador protege o valor automaticamente.
O programa abaixo, extraído diretamente da seção 4 do livro de referência do curso, demonstra as duas abordagens lado a lado calculando a área de um círculo. O resultado é idêntico, mas os mecanismos internos são bem diferentes:
#include <stdio.h> #define PI_MACRO 3.14159 // substituição textual — sem tipo, sem memória int main() { const float pi_const = 3.14159; // variável somente leitura — tem tipo e endereço float radius = 5.0; float area_macro = PI_MACRO * radius * radius; float area_const = pi_const * radius * radius; printf("Area using macro: %.2f\n", area_macro); printf("Area using const: %.2f\n", area_const); return 0; }
Area using macro: 78.54
Area using const: 78.54
PI_MACRO é substituído por 3.14159 antes do compilador ver o código; já pi_const é uma variável real alocada na memória, com tipo float verificado pelo compilador.
Ambas as formas são amplamente usadas na prática. A escolha depende do contexto:
| Característica | #define |
const |
|---|---|---|
| Etapa de processamento | Pré-processador (antes da compilação) | Compilador |
| Tem tipo de dado | Não — é substituição textual pura | Sim — tipo explícito e verificado |
| Ocupa memória | Não — não existe em runtime | Sim — é uma variável na memória |
| Verificação de tipo | Nenhuma — erros de tipo só aparecem no uso | Completa — o compilador valida o tipo |
| Visível no depurador | Não — o depurador vê apenas o valor numérico | Sim — o nome da variável é preservado |
| Escopo | Do ponto de definição até o fim do arquivo | Segue as regras normais de escopo de bloco |
| Pode ser ponteiro | Não | Sim — const int *ptr |
| Uso preferido | Inclusão condicional, macros parametrizadas, constantes globais simples | Constantes com tipo definido, escopo controlado |
const — ele oferece verificação de tipo, escopo controlado e melhor integração com depuradores. O material de referência deste curso usa #define extensivamente (como #define alturaMaxima 225), então é fundamental conhecer os dois.
O #define tem outros usos importantes além de definir constantes numéricas. Um dos mais comuns são os include guards (guardas de inclusão), que evitam que um arquivo de cabeçalho seja incluído múltiplas vezes em um projeto — tema que aprofundaremos nas Aulas 17 e 18:
/* arquivo: geometria.h */ #ifndef GEOMETRIA_H // "se GEOMETRIA_H NÃO estiver definido..." #define GEOMETRIA_H // "...define-o agora" #define PI 3.14159 float areaCirculo(float r); float areaRetangulo(float b, float h); #endif // fim do bloco condicional
geometria.h é incluído, GEOMETRIA_H não está definido, então o bloco inteiro é processado e GEOMETRIA_H é definido. Se o arquivo for incluído novamente, GEOMETRIA_H já existe e todo o conteúdo é ignorado, evitando erros de declaração dupla.
O programa abaixo usa tanto #define quanto const para calcular propriedades de um movimento uniformemente acelerado — tema diretamente relacionado ao contexto científico do curso:
#include <stdio.h> #include <math.h> // Constantes físicas globais — #define por serem simples e globais #define GRAVIDADE 9.81 // m/s² #define LUZ 299792458 // m/s int main() { // Constantes locais com tipo — const para segurança const float altura_inicial = 100.0f; // metros const float velocidade_0 = 0.0f; // m/s (largado do repouso) // Tempo de queda: h = v0*t + (g/2)*t² → t = sqrt(2h/g) float tempo = sqrt(2.0 * altura_inicial / GRAVIDADE); float velocidade_final = velocidade_0 + GRAVIDADE * tempo; printf("Queda de %.0f metros:\n", altura_inicial); printf(" Tempo de queda: %.2f s\n", tempo); printf(" Velocidade final: %.2f m/s\n", velocidade_final); printf("\nVelocidade da luz: %d m/s\n", LUZ); return 0; }
Queda de 100 metros:
Tempo de queda: 4.52 s
Velocidade final: 44.29 m/s
Velocidade da luz: 299792458 m/s
Em Java, o equivalente mais próximo de uma constante é uma variável declarada com static final. O C não tem essa combinação — as duas abordagens disponíveis são #define e const:
| Aspecto | C — #define |
C — const |
Java — static final |
|---|---|---|---|
| Declaração | #define PI 3.14 |
const float PI = 3.14; |
static final double PI = 3.14; |
| Tem tipo | Não | Sim | Sim |
| Verificação em compilação | Não (só no uso) | Sim | Sim |
| Escopo | Global no arquivo | Bloco onde foi declarada | Classe |
| Convenção de nome | MAIUSCULAS |
Variável ou MAIUSCULAS |
MAIUSCULAS_COM_UNDERSCORE |
| C | Java |
|---|---|
#define PI_MACRO 3.14159
int main() {
const float pi = 3.14159f;
float r = 5.0;
float area = pi * r * r;
printf("%.2f\n", area);
}
|
public class Main {
static final double PI = 3.14159;
public static void main(String[] args) {
double r = 5.0;
double area = PI * r * r;
System.out.printf("%.2f%n", area);
}
}
|
Teste seus conhecimentos sobre constantes em C: