Constantes são valores que não mudam durante a execução do programa. Em C, existem várias maneiras de defini-las, cada uma com suas vantagens e contextos de uso. Nesta aula, exploraremos as principais formas: a palavra-chave const, a diretiva de pré-processador #define, enumerações (enum) e literais. Compreender essas diferenças é essencial para escrever código claro, seguro e de fácil manutenção.

Além disso, veremos como constantes podem tornar o código mais legível e evitar erros, especialmente em programas maiores. Vamos mergulhar em cada uma delas.

const

A palavra-chave const é usada para declarar variáveis cujo valor não pode ser alterado após a inicialização. É uma forma segura e com verificação de tipo, pois o compilador garante que a variável não seja modificada. Diferente de #define, const respeita escopo e tipos.

Exemplo:

const int MAX = 100;
const float PI = 3.14159;
const char MENSAGEM[] = "Olá, mundo!";

Note que variáveis const devem ser inicializadas no momento da declaração. Em C, const não cria uma constante de tempo de compilação (não pode ser usada em tamanhos de array estático, por exemplo), mas ainda assim é útil para valores imutáveis.

#define

A diretiva #define é uma instrução de pré-processador que substitui um identificador por um texto antes da compilação. É frequentemente usada para definir constantes simbólicas, macros e até mesmo pequenas funções. Por não ter verificação de tipo, deve ser usada com cuidado.

Exemplo:

#define MAX 100
#define PI 3.14159
#define AREA_CIRCULO(r) (PI * (r) * (r))

O pré-processador substitui todas as ocorrências de MAX por 100 antes da compilação. Diferente de const, #define não aloca memória e pode ser usado em contextos que exigem constantes de tempo de compilação, como tamanhos de arrays estáticos.

Boas práticas: use letras maiúsculas para constantes definidas com #define e parênteses em macros para evitar problemas de precedência.

Enumerações (enum)

Enumerações são tipos definidos pelo usuário que consistem em um conjunto de constantes inteiras nomeadas. São ideais para representar um conjunto de opções ou estados, tornando o código mais legível e menos propenso a erros.

Exemplo:

enum Dias {SEG, TER, QUA, QUI, SEX, SAB, DOM};
enum Dias hoje = SEG;

enum Estado {LIGADO = 1, DESLIGADO = 0, INDEFINIDO = -1};

Por padrão, os valores começam em 0 e incrementam de 1 em 1, mas podem ser explicitamente atribuídos. Enumerações são úteis em switches e para evitar números mágicos.

Literais

Literais são valores constantes escritos diretamente no código. Eles representam valores fixos de tipos básicos como inteiros, ponto flutuante, caracteres e strings. Exemplos:

42          // literal inteiro (int)
3.14        // literal double
'a'         // literal caractere (char)
"Hello"    // literal string (array de char)
0xFF        // literal hexadecimal
0777        // literal octal
1e-3        // literal científico

Literais são imutáveis por natureza. Em C, literais string são armazenados em área somente leitura, e tentar modificá-los resulta em comportamento indefinido.

Boas práticas e observações finais

Preferir const em vez de #define para constantes tipadas, a menos que precise de uma constante de tempo de compilação. Use enumerações para conjuntos de valores relacionados. Evite números mágicos; sempre nomeie constantes para melhorar a legibilidade. Lembre-se de que #define não tem escopo, enquanto const e enum respeitam escopo de bloco.

Referências

Exercícios

  1. Declare uma constante const chamada TAXA_JUROS com valor 0.05 e uma variável double chamada saldo inicializada com 1000.0. Calcule e imprima o saldo após aplicar a taxa (saldo * (1 + TAXA_JUROS)).

    ✓ Resposta:
    #include <stdio.h>
    
    int main() {
        const double TAXA_JUROS = 0.05;
        double saldo = 1000.0;
        double novo_saldo = saldo * (1 + TAXA_JUROS);
        printf("Novo saldo: %.2f\n", novo_saldo);
        return 0;
    }
  2. Usando #define, crie uma macro que calcula o quadrado de um número. Depois, use-a para imprimir o quadrado de 7.

    ✓ Resposta:
    #include <stdio.h>
    #define QUADRADO(x) ((x) * (x))
    
    int main() {
        printf("%d\n", QUADRADO(7));
        return 0;
    }
  3. Defina uma enumeração enum Meses com os meses do ano (JAN a DEZ). Atribua a JAN o valor 1. Imprima o valor de DEZ.

    ✓ Resposta:
    #include <stdio.h>
    
    enum Meses { JAN = 1, FEV, MAR, ABR, MAI, JUN, JUL, AGO, SET, OUT, NOV, DEZ };
    
    int main() {
        printf("%d\n", DEZ);
        return 0;
    }
  4. Escreva um programa que use literais para calcular a área de um círculo de raio 5. Use o literal 3.14159 para PI.

    ✓ Resposta:
    #include <stdio.h>
    
    int main() {
        double raio = 5;
        double area = 3.14159 * raio * raio;
        printf("Área: %.2f\n", area);
        return 0;
    }
  5. Explique por que usar const int é preferível a #define para definir uma constante inteira em um programa com múltiplos arquivos. Dê um exemplo de código.

    ✓ Resposta:

    const int tem escopo e tipo, prevenindo erros de substituição inesperada e permitindo que o compilador verifique tipos. Já #define é uma substituição textual sem escopo, podendo causar conflitos. Em múltiplos arquivos, const pode ser usada com extern para compartilhar, enquanto #define precisa ser redefinida ou incluída em cada arquivo. Exemplo:

    // arquivo config.h
    extern const int MAX_USUARIOS;
    
    // arquivo config.c
    const int MAX_USUARIOS = 100;
    
    // main.c
    #include "config.h"
    int main() {
        int arr[MAX_USUARIOS]; // erro: não é constante de tempo de compilação
        // melhor usar #define para tamanho de array estático
    }