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.
- 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
#definefunciona: 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
constexprdo C23: uma constante de verdade, calculada na compilação. - O
enumcomo 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 constante | Com 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_SUCOguarda um número com vírgula. Oconstpode 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_SUCOjá 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
doublecom 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
semananem 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 oconstfez 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
- "
globaltem 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 domain,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).
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
#defineaté 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
intque o compilador conhece enquanto compila. Pode ser usada como tamanho de array, emcase, 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; comconstexpr, 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 desizeof.
$ 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é umdouble, e umfloatnão consegue guardar exatamente o mesmo valor (aula float e double). Conserto:constexpr float F = 0.1f;(offaz o número já nascerfloat) ouconstexpr 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; escreva3.
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
| Forma | Tem tipo? | Constante de verdade (array global, case)? | Respeita as chaves { }? | Quando usar |
|---|---|---|---|---|
const int X = 7; | Sim | Não (no gcc) | Sim | Valores 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; C23 | Sim | Sim | Sim | A escolha do guia para valores fixos (limites, preços, tamanhos), quando o compilador é C23. |
#define X 7 | Não (troca de texto) | Sim | Nã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) | Sim | Sim | Constantes inteiras em C antigo, sem os riscos do #define; e listas de opções. |
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.
#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
| Escrita | O 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 10 | Troca 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.c | Mostra 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:
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,
constnão é constante de verdade: não serve para tamanho de array global nemcaseno gcc. #define X 7: o pré-processador troca o texto antes de compilar (gcc -Emostra). 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:
constexprpara valores fixos;constpara valores que surgem com o programa rodando; saiba ler#define, que está em todo código C.