C Constantes

Alguns valores nunca deveriam mudar enquanto o programa roda: os 7 dias da semana, o preço do suco na lojinha da Ana, o número máximo de alunos de uma turma. Em C, você pode avisar isso ao compilador, e ele passa a recusar qualquer linha que tente mudar esse valor. Nesta aula você vai conhecer as três formas de criar uma constante em C (const, #define e constexpr), mais uma quarta que aparece muito (enum), e aprender qual usar em cada caso.

Você vai aprender
  • Por que dar nome a um número em vez de escrevê-lo solto no código.
  • Como criar uma variável que não muda com const, e o erro real quando alguém tenta mudá-la.
  • Como o #define funciona: troca de texto antes da compilação, sem tipo e sem ;.
  • As duas armadilhas clássicas do #define: o ; no fim e a conta sem parênteses.
  • O constexpr do C23: uma constante de verdade, calculada na compilação.
  • O enum como um jeito antigo de criar constantes inteiras.
  • Qual das formas usar em cada situação.

Todos os programas desta aula foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa (cada opção é explicada em Compilar com gcc). Você pode repetir cada um no editor do guia, que compila C de verdade no navegador.


A ideia: a etiqueta com valor impresso

Na aula Variáveis, uma variável era uma caixa com etiqueta: você podia tirar o que estava dentro e pôr outra coisa. Agora imagine uma caixa lacrada, com o conteúdo à mostra e um aviso: "não abrir". Você pode olhar e usar o valor quantas vezes quiser, mas não pode trocá-lo.

Isso é uma constante: um valor com nome que não muda. Ela resolve dois problemas:

Sem constanteCom constante
gasto = 4.50 * 7;
O que é 4.50? O que é 7? Quem lê precisa adivinhar.
gasto = PRECO_SUCO * DIAS_SEMANA;
O código se explica sozinho.
O preço subiu para 5.00: você precisa achar todo 4.50 do programa (e torcer para não esquecer nenhum, nem trocar um 4.50 que era outra coisa).Você muda uma linha, a da constante, e o programa inteiro passa a usar o valor novo.
Uma linha errada pode mudar o valor sem querer (preco = 0;).O compilador recusa a linha errada.

Programadores chamam um número solto no meio do código, sem explicação, de número mágico (magic number): ele "funciona", mas ninguém sabe por quê. Constantes acabam com os números mágicos.


1. const: uma variável que não muda

A forma mais simples é escrever a palavra const (de constant, "constante") antes do tipo de uma variável:

#include <stdio.h>

int main(void) {
    const double PRECO_SUCO = 4.50;
    const int DIAS_SEMANA = 7;
    double gasto = PRECO_SUCO * DIAS_SEMANA;
    printf("Suco por dia: R$ %.2f\n", PRECO_SUCO);
    printf("Na semana: R$ %.2f\n", gasto);
    return 0;
}
const
"Esta variável é só de leitura." Depois de receber o valor inicial, ela não pode mais ser mudada.
double
O tipo, como em qualquer variável: PRECO_SUCO guarda um número com vírgula. O const pode ir antes de qualquer tipo (const int, const char…).
PRECO_SUCO
O nome. Constantes costumam ser escritas em MAIÚSCULAS, com _ separando as palavras. O compilador não exige, mas é um costume tão forte que quem lê PRECO_SUCO já sabe que é uma constante.
= 4.50
O valor. É a única chance de dar valor a uma constante: na própria declaração.
PRECO_SUCO * DIAS_SEMANA
Ler uma constante é igual a ler uma variável: basta usar o nome. O * é a multiplicação (aula Operadores Aritméticos).
%.2f
Mostra um double com 2 casas depois do ponto (todos os formatos estão em Formatos do printf).
$ gcc -std=c23 -Wall -Wextra lanche.c -o lanche
$ ./lanche
Suco por dia: R$ 4.50
Na semana: R$ 31.50

Nada de novo na saída: o programa funciona igual ao que você escreveria com variáveis comuns. A diferença aparece quando alguém tenta mudar o valor.

Tentar mudar uma constante

#include <stdio.h>

int main(void) {
    const int DIAS_SEMANA = 7;
    DIAS_SEMANA = 8;
    printf("%d\n", DIAS_SEMANA);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra semana.c -o semana
semana.c: In function ‘main’:
semana.c:5:17: error: assignment of read-only variable ‘DIAS_SEMANA’
    5 |     DIAS_SEMANA = 8;
      |                 ^
semana.c:5:17
Arquivo, linha 5, coluna 17: o compilador aponta o = da linha que tenta mudar o valor.
error:
É um erro, não um aviso: o programa semana nem foi gerado.
assignment of read-only variable
"Atribuição a uma variável só de leitura." Assignment (atribuição) é guardar um valor com =; read-only é "só de leitura", o que o const fez com ela.
‘DIAS_SEMANA’
O nome da constante, entre as aspas curvas que o gcc usa.

Essa é a grande vantagem do const: um engano vira um erro de compilação, que aparece na hora, em vez de um número errado que só alguém descobre muito depois. O Clang, que roda no editor do guia, diz a mesma coisa com outras palavras: cannot assign to variable 'DIAS_SEMANA' with const-qualified type 'const int' ("não é possível atribuir à variável, que tem um tipo marcado como const"), e ainda aponta a linha da declaração com note: variable 'DIAS_SEMANA' declared const here.

Missão: o programa abaixo tem um engano: uma linha zera o preço do café. Rode e veja o total errado. Agora "lacre" o preço: escreva const antes de double preco_cafe e rode de novo. O compilador tem de recusar o programa e apontar a linha do engano.

#include <stdio.h>

int main(void) {
    double preco_cafe = 3.50;
    int xicaras = 4;

    preco_cafe = 0;   // engano: alguém zerou o preço

    printf("Total: R$ %.2f\n", preco_cafe * xicaras);
    return 0;
}
#include <stdio.h>

int main(void) {
    const double preco_cafe = 3.50;
    int xicaras = 4;

    preco_cafe = 0;   // engano: alguém zerou o preço

    printf("Total: R$ %.2f\n", preco_cafe * xicaras);
    return 0;
}

Depois de ver o erro, apague a linha do engano: o programa volta a compilar e mostra Total: R$ 14.00. É assim no dia a dia: o const não conserta nada sozinho, ele mostra onde está o problema.

Uma constante sem valor

Em C (diferente do C++), declarar um const sem valor não é erro. Mas a constante fica com lixo para sempre, porque depois você não pode mais dar valor a ela:

$ cat semvalor.c
#include <stdio.h>

int main(void) {
    const int LIMITE;
    printf("%d\n", LIMITE);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra semvalor.c -o semvalor
semvalor.c: In function ‘main’:
semvalor.c:5:5: warning: ‘LIMITE’ is used uninitialized [-Wuninitialized]
    5 |     printf("%d\n", LIMITE);
      |     ^~~~~~~~~~~~~~~~~~~~~~
semvalor.c:4:15: note: ‘LIMITE’ was declared here
    4 |     const int LIMITE;
      |               ^~~~~~
$ ./semvalor
0                        « saiu 0 por acaso: o valor é lixo

‘LIMITE’ is used uninitialized = "LIMITE é usada sem ter recebido valor". É só um aviso, então o programa foi gerado, mas ele não faz sentido. O Clang do editor é mais direto e avisa já na declaração: default initialization of an object of type 'const int' leaves the object uninitialized ("a inicialização padrão deixa o objeto sem valor"). Regra: todo const nasce com valor.

O const do C não é uma constante "de verdade"

Aqui está uma surpresa que pega até quem já programa. Para o C, um const int é uma variável que não pode ser mudada, mas não é uma "expressão constante": o compilador não usa o valor dela nos lugares que exigem um número conhecido antes do programa rodar. Dois desses lugares são o tamanho de um array global (aula Arrays) e o rótulo de um case (aula switch):

#include <stdio.h>

const int TAM = 5;
int global[TAM];

int main(void) {
    const int N = 3;
    int v[N] = {1, 2, 3};
    printf("%d\n", v[0]);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra tamanho.c -o tamanho
tamanho.c:4:5: error: variably modified ‘global’ at file scope
    4 | int global[TAM];
      |     ^~~~~~
tamanho.c: In function ‘main’:
tamanho.c:8:16: error: variable-sized object may not be initialized except with an empty initializer
    8 |     int v[N] = {1, 2, 3};
      |                ^
variably modified ‘global’ at file scope
"global tem tamanho variável, fora de qualquer função." Para o gcc, TAM é uma variável, então o array teria tamanho variável, e isso não é permitido fora de funções.
variable-sized object may not be initialized…
"Um objeto de tamanho variável não pode receber valores iniciais (só o inicializador vazio {})." Dentro do main, int v[N] virou um array de tamanho variável (VLA, aula Arrays), e esse tipo de array não aceita = {1, 2, 3}.

No C++, esse mesmo código compila, porque lá um const int com valor conhecido conta como constante. No C, não. Por isso, durante quase 50 anos, programadores C usaram o #define (a próxima seção) para esses casos. O C23 trouxe uma solução melhor, o constexpr (seção 3).

No editor do guia

O Clang do editor é mais tolerante: ele aceita o tamanho.c como uma extensão (algo além do que a linguagem promete) e só avisa: variable length array folded to constant array as an extension [-Wgnu-folding-constant] ("array de tamanho variável transformado em array de tamanho fixo, como extensão"). O case com um const int também passa no Clang e é recusado pelo gcc (case label does not reduce to an integer constant). Código que depende de extensão de um compilador pode quebrar no outro: use constexpr, #define ou enum nesses lugares.


2. #define: trocar um nome por um texto

A forma mais antiga, e que você vai ver em quase todo código C, é o #define ("definir"):

#include <stdio.h>

#define PRECO_SUCO 4.50
#define DIAS_SEMANA 7
#define LOJA "Lojinha da Ana"

int main(void) {
    double gasto = PRECO_SUCO * DIAS_SEMANA;
    printf("%s\n", LOJA);
    printf("Na semana: R$ %.2f\n", gasto);
    return 0;
}
#define
Uma ordem para o pré-processador, a etapa que lê o seu arquivo antes do compilador (a mesma que trata o #include; veja Como o C Vira Programa). Toda linha que começa com # é dele.
PRECO_SUCO
O nome que será procurado no código. Também em MAIÚSCULAS, por costume.
4.50
O texto que vai substituir o nome. Repare: sem tipo (não há double), sem = e sem ; no fim.
"Lojinha da Ana"
O texto pode ser qualquer coisa: um número, um texto entre aspas, até um pedaço de código.
$ gcc -std=c23 -Wall -Wextra define.c -o define
$ ./define
Lojinha da Ana
Na semana: R$ 31.50

O que o pré-processador faz de verdade

O #define não cria variável nenhuma. O pré-processador simplesmente procura o nome e troca pelo texto, como o "Localizar e substituir" de um editor de texto. A opção -E do gcc mostra o arquivo depois dessa troca, do jeito que o compilador o recebe (o tail -8 mostra só as 8 últimas linhas, porque o stdio.h despeja centenas de linhas antes):

$ gcc -E define.c | tail -8

# 7 "define.c"
int main(void) {
    double gasto = 4.50 * 7;
    printf("%s\n", "Lojinha da Ana");
    printf("Na semana: R$ %.2f\n", gasto);
    return 0;
}

Os nomes PRECO_SUCO, DIAS_SEMANA e LOJA sumiram: no lugar deles estão 4.50, 7 e "Lojinha da Ana". As linhas #define também sumiram. A linha # 7 "define.c" é um recado do pré-processador para o compilador ("o que vem a seguir é a linha 7 de define.c"), para que as mensagens de erro apontem a linha certa do seu arquivo.

Como o compilador só vê o número, o #define funciona em qualquer lugar que exija uma constante: tamanho de array global, case, tudo. E como é só troca de texto, ele também tem pegadinhas.

Armadilha 1: o ; no fim

Quem está acostumado a terminar tudo com ; escreve sem pensar:

#include <stdio.h>
#define PRECO 2.50;

int main(void) {
    double total = PRECO * 3;
    printf("%.2f\n", total);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra pontovirgula.c -o pontovirgula
pontovirgula.c: In function ‘main’:
pontovirgula.c:5:26: error: invalid type argument of unary ‘*’ (have ‘int’)
    5 |     double total = PRECO * 3;
      |                          ^~~

A mensagem parece não ter nada a ver com o #define. Faça a troca de texto na cabeça: PRECO vira 2.50;, e a linha fica double total = 2.50; * 3;. O ; encerrou a declaração no meio, e sobrou um * 3; solto. Um * sozinho na frente de um valor tem outro significado em C (é usado com ponteiros, aula Ponteiros), e o gcc reclama que não dá para usá-lo com um int. Invalid type argument of unary ‘*’ = "tipo de argumento inválido para o * de um operando só".

Conserto: #define PRECO 2.50, sem ;. Se um erro estranho aparece numa linha que usa um nome em MAIÚSCULAS, olhe o #define dele.

Armadilha 2: a conta sem parênteses

Esta é pior, porque compila sem nenhum aviso:

#include <stdio.h>
#define CINCO 2 + 3

int main(void) {
    int x = CINCO * 10;
    printf("%d\n", x);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra conta.c -o conta
$ ./conta
32                       « esperava 50

Troque o texto: int x = 2 + 3 * 10;. A multiplicação é feita antes da soma (aula Precedência), então o resultado é 2 + 30 = 32. O pré-processador não sabe fazer contas; ele só cola o texto. Conserto: sempre que o texto de um #define for uma conta, ponha parênteses em volta: #define CINCO (2 + 3). Aí a linha vira (2 + 3) * 10, que dá 50.

Outras diferenças do #define

  • Não tem tipo. O tipo vem do texto: 7 é int, 4.50 é double, "Ana" é texto.
  • Vale do #define até o fim do arquivo, inclusive dentro de outras funções. Ele não respeita as chaves { } (diferente das variáveis, veja Escopo).
  • Pode ser redefinido, com só um aviso:
$ cat redef.c
#include <stdio.h>

#define MAX 10
#define MAX 20

int main(void) {
    printf("%d\n", MAX);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra redef.c -o redef
redef.c:4:9: warning: ‘MAX’ redefined
    4 | #define MAX 20
      |         ^~~
redef.c:3:9: note: this is the location of the previous definition
    3 | #define MAX 10
      |         ^~~
$ ./redef
20

‘MAX’ redefined = "MAX foi redefinido"; this is the location of the previous definition = "aqui está a definição anterior". Vale a última. O #define tem muito mais recursos (inclusive "macros" que recebem parâmetros), vistos em Pré-processador e Macros com Parâmetros.

Missão: a Ana vende 3 cadernos a R$ 2,50 e o programa deveria mostrar Total: R$ 7.50. Mas os dois #define têm problemas: um não deixa o programa compilar e o outro faz a conta sair errada. Rode, conserte um de cada vez (só nas linhas do #define) e confira o total.

#include <stdio.h>

#define PRECO_CADERNO 2.50;
#define CADERNOS 2 + 1

int main(void) {
    double total = CADERNOS * PRECO_CADERNO;
    printf("Total: R$ %.2f\n", total);
    return 0;
}
#include <stdio.h>

#define PRECO_CADERNO 2.50
#define CADERNOS (2 + 1)

int main(void) {
    double total = CADERNOS * PRECO_CADERNO;
    printf("Total: R$ %.2f\n", total);
    return 0;
}

Se você tirou só o ;, o programa compilou e mostrou Total: R$ 4.50: a linha virou 2 + 1 * 2.50. Com os parênteses, (2 + 1) * 2.50 dá os 7.50 certos.


3. constexpr: a constante de verdade C23

O C23 trouxe do C++ a palavra constexpr (de constant expression, "expressão constante"). Ela junta o melhor dos dois mundos: tem tipo, como o const, e é uma constante de verdade, conhecida na compilação, como o #define.

#include <stdio.h>

int main(void) {
    constexpr int MAX = 10;
    constexpr double PI = 3.14159;
    int notas[MAX];
    printf("%d %f %zu\n", MAX, PI, sizeof notas);
    return 0;
}
constexpr int MAX = 10;
Uma constante int que o compilador conhece enquanto compila. Pode ser usada como tamanho de array, em case, em qualquer lugar.
int notas[MAX];
Um array de 10 inteiros, de tamanho fixo. Com const int MAX, o gcc faria um array de tamanho variável; com constexpr, não.
sizeof notas
Quantos bytes o array ocupa: 10 inteiros de 4 bytes = 40 (aula Tamanho dos Tipos). O %zu é o formato do resultado de sizeof.
$ gcc -std=c23 -Wall -Wextra turma.c -o turma
$ ./turma
10 3.141590 40

O valor tem de ser conhecido na compilação

"Constante de verdade" quer dizer que o compilador precisa conseguir calcular o valor sem rodar o programa. Contas com números e com outras constexpr servem (constexpr int DOBRO = MAX * 2;). Uma variável comum, não:

$ cat dobro.c
#include <stdio.h>

int main(void) {
    int n = 5;
    constexpr int DOBRO = n * 2;
    printf("%d\n", DOBRO);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra dobro.c -o dobro
dobro.c: In function ‘main’:
dobro.c:5:27: error: initializer element is not constant
    5 |     constexpr int DOBRO = n * 2;
      |                           ^

Initializer element is not constant = "o valor inicial não é constante". O n é uma variável: o valor dela poderia mudar antes daquela linha. O Clang diz: constexpr variable 'DOBRO' must be initialized by a constant expression ("uma variável constexpr precisa ser inicializada com uma expressão constante"). Se o valor só existe com o programa rodando (por exemplo, digitado pelo usuário), use const.

O constexpr do C é exigente com os tipos

O constexpr do C recusa valores que mudariam ao entrar no tipo, coisa que const e variáveis comuns aceitam em silêncio:

$ cat exigente.c
#include <stdio.h>

int main(void) {
    constexpr float F = 0.1;
    constexpr int I = 3.0;
    printf("%f %d\n", F, I);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra exigente.c -o exigente
exigente.c: In function ‘main’:
exigente.c:4:25: error: ‘constexpr’ initializer not representable in type of object
    4 |     constexpr float F = 0.1;
      |                         ^~~
exigente.c:5:23: error: ‘constexpr’ integer initializer is not an integer constant expression
    5 |     constexpr int I = 3.0;
      |                       ^~~
  • Initializer not representable in type of object = "o valor inicial não cabe exatamente no tipo". 0.1 é um double, e um float não consegue guardar exatamente o mesmo valor (aula float e double). Conserto: constexpr float F = 0.1f; (o f faz o número já nascer float) ou constexpr double F = 0.1;.
  • Integer initializer is not an integer constant expression = "o valor inicial de um inteiro não é uma expressão inteira". 3.0 é um número com vírgula; escreva 3.

Isso é proposital: uma constante constexpr tem exatamente o valor que você escreveu, sem cortes escondidos.

E nos compiladores antigos?

O constexpr só existe a partir do C23. Com -std=c17, o gcc nem reconhece a palavra:

$ gcc -std=c17 -Wall -Wextra turma.c -o turma
turma.c: In function ‘main’:
turma.c:4:5: error: ‘constexpr’ undeclared (first use in this function)
    4 |     constexpr int MAX = 10;
      |     ^~~~~~~~~
turma.c:4:5: note: each undeclared identifier is reported only once for each function it appears in
turma.c:4:14: error: expected ‘;’ before ‘int’
    4 |     constexpr int MAX = 10;
      |              ^~~~
      |              ;   « seguem mais erros: MAX e PI também viram "não declarados"

Para o C17, constexpr é só um nome desconhecido (undeclared, "não declarado"). Se o seu código precisa compilar em compiladores antigos, use #define ou enum. O gcc 15 e o editor do guia já usam C23.


4. enum: constantes inteiras com nome

Antes do constexpr, havia um truque muito usado para criar constantes inteiras com as regras da linguagem (e não por troca de texto): o enum ("enumeração").

#include <stdio.h>

enum { MAX_ALUNOS = 30, MAX_NOTAS = 4 };

int main(void) {
    int notas[MAX_ALUNOS][MAX_NOTAS] = {0};
    printf("Cabem %d alunos com %d notas cada.\n", MAX_ALUNOS, MAX_NOTAS);
    printf("%d\n", notas[0][0]);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra enum.c -o enum
$ ./enum
Cabem 30 alunos com 4 notas cada.
0

Os nomes dentro do enum { … } são constantes inteiras de verdade: servem para tamanho de array (aqui, uma tabela de 30 linhas por 4 colunas, aula Arrays Multidimensionais) e para case. A limitação: só valem para inteiros. O uso principal do enum é outro, dar nomes a uma lista de opções (SEGUNDA, TERCA…), e está na aula Enums.


5. Formas diferentes: qual usar

Este programa calcula a mesma coisa de três jeitos. Os três mostram 31.50:

#include <stdio.h>

#define DIAS_A 7

int main(void) {
    const int DIAS_B = 7;
    constexpr int DIAS_C = 7;
    printf("%.2f %.2f %.2f\n", 4.50 * DIAS_A, 4.50 * DIAS_B, 4.50 * DIAS_C);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra tres.c -o tres
$ ./tres
31.50 31.50 31.50
FormaTem tipo?Constante de verdade (array global, case)?Respeita as chaves { }?Quando usar
const int X = 7;SimNão (no gcc)SimValores que não devem mudar, mas que só são conhecidos com o programa rodando (por exemplo, calculados a partir do que o usuário digitou).
constexpr int X = 7; C23SimSimSimA escolha do guia para valores fixos (limites, preços, tamanhos), quando o compilador é C23.
#define X 7Não (troca de texto)SimNão (vale até o fim do arquivo)Código que precisa compilar em C antigo; e coisas que só o pré-processador faz (aulas Macros e Compilação Condicional). É o que você mais vai encontrar em código existente.
enum { X = 7 };Sim (int)SimSimConstantes inteiras em C antigo, sem os riscos do #define; e listas de opções.
Na dúvida

Para um número fixo, use constexpr. Se o valor depende do programa rodando, use const. Saiba ler #define, porque ele está em toda parte (até no stdio.h: o EOF e o BUFSIZ são #defines).


Quando dá errado

error: assignment of read-only variable ‘X’

O que significa: uma linha tenta mudar com = (ou +=, ++…) uma variável declarada com const. No Clang: cannot assign to variable 'X' with const-qualified type.

Como consertar: se o valor realmente não deveria mudar, a linha apontada é o engano: apague-a ou corrija-a. Se o valor precisa mudar, ele não é uma constante: tire o const.

error: invalid type argument of unary ‘*’ (ou expected expression) numa linha que parece certa

O que significa: quase sempre é um #define terminado com ; (ou com =, como #define MAX = 10). A troca de texto colou o ; no meio da linha.

Como consertar: olhe o #define do nome em MAIÚSCULAS que aparece na linha e deixe só #define NOME valor. Para ver o que o compilador recebeu, rode gcc -E arquivo.c | tail.

Conta com #define dá resultado errado, sem aviso

O que significa: o texto do #define é uma conta sem parênteses (#define CINCO 2 + 3), e a precedência dos operadores misturou tudo.

Como consertar: parênteses em volta: #define CINCO (2 + 3). Ou troque por constexpr int CINCO = 2 + 3;, que é uma variável de verdade e não tem esse problema.

error: variably modified ‘v’ at file scope / case label does not reduce to an integer constant

O que significa: você usou um const int num lugar que exige uma constante de verdade. No C, const não basta.

Como consertar: troque const por constexpr (C23), ou use #define ou enum.

error: initializer element is not constant num constexpr

O que significa: o valor do constexpr usa uma variável comum, que só tem valor com o programa rodando.

Como consertar: use só números e outras constexpr na conta. Se o valor depende do programa rodando, use const.

error: ‘constexpr’ undeclared (first use in this function)

O que significa: o compilador está num padrão anterior ao C23 (-std=c17, -std=c99…) ou é antigo demais.

Como consertar: compile com -std=c23, ou use #define/enum para continuar compatível.


Referência rápida

EscritaO que faz
const double PRECO = 4.50;Variável só de leitura. Mudar depois é erro (read-only variable).
constexpr int MAX = 10;C23 Constante com tipo, calculada na compilação. Serve para tamanho de array e case.
constexpr int DOBRO = MAX * 2;Conta com outras constantes: permitido.
#define MAX 10Troca o texto MAX por 10 antes de compilar. Sem tipo, sem =, sem ;.
#define CINCO (2 + 3)Conta num #define: sempre entre parênteses.
#define LOJA "Lojinha"Um texto com nome.
enum { MAX = 10 };Constante inteira, válida em qualquer versão do C.
gcc -E arquivo.cMostra o código depois da troca de texto do pré-processador.

Exercícios

1. Lacrar a caixa

Complete para que LIMITE não possa mais ser mudado:

int LIMITE = 100;

2. O #define certo

Complete a linha para que TAXA seja trocado por 5 (lembre-se do que não vai num #define):

3. Com #define DEZ 5 + 5, quanto vale DEZ * 2?

4. Qual destas declarações o gcc 15 recusa fora de qualquer função?

5. O que acontece com const int x = 1; seguido de x = 2;?

6. Qual afirmação sobre #define é verdadeira?

Missão final: aposente os números mágicos

Missão: a Ana guarda R$ 2,50 por dia no cofrinho. O programa foi escrito para um mês de 30 dias, com os números espalhados. Agora o mês tem 31 dias. Crie duas constantes, DIAS (valendo 31) e POR_DIA (valendo 2.50), do jeito que preferir (constexpr, const ou #define), e troque todos os números mágicos por elas. A saída deve ficar como abaixo, sem avisos.

Dias no mês: 31
Guardado por dia: R$ 2.50
Total no mês: R$ 77.50
Em 2 meses: R$ 155.00
#include <stdio.h>

int main(void) {
    printf("Dias no mês: %d\n", 30);
    printf("Guardado por dia: R$ %.2f\n", 2.50);
    printf("Total no mês: R$ %.2f\n", 30 * 2.50);
    printf("Em 2 meses: R$ %.2f\n", 2 * 30 * 2.50);
    return 0;
}
#include <stdio.h>

int main(void) {
    constexpr int DIAS = 31;
    constexpr double POR_DIA = 2.50;

    printf("Dias no mês: %d\n", DIAS);
    printf("Guardado por dia: R$ %.2f\n", POR_DIA);
    printf("Total no mês: R$ %.2f\n", DIAS * POR_DIA);
    printf("Em 2 meses: R$ %.2f\n", 2 * DIAS * POR_DIA);
    return 0;
}

Repare que o 2 de "2 meses" pode continuar no código: ali ele não é mágico, o texto ao lado já explica o que é. Número mágico é o que obriga quem lê a adivinhar. E da próxima vez que o mês mudar, você troca uma linha.


Resumo

  • Constante = valor com nome que não muda. Acaba com os números mágicos e concentra a mudança numa linha só. Por costume, nome em MAIÚSCULAS.
  • const int X = 7;: variável só de leitura. Mudar depois dá assignment of read-only variable. Sempre dê o valor na declaração.
  • No C, const não é constante de verdade: não serve para tamanho de array global nem case no gcc.
  • #define X 7: o pré-processador troca o texto antes de compilar (gcc -E mostra). Sem tipo, sem =, sem ;; conta sempre entre parênteses.
  • constexpr int X = 7; C23: tem tipo e é constante de verdade; o valor precisa ser conhecido na compilação e caber exatamente no tipo.
  • enum { X = 7 };: constante inteira que funciona em qualquer C.
  • Qual usar: constexpr para valores fixos; const para valores que surgem com o programa rodando; saiba ler #define, que está em todo código C.