Const Correctness
Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 8 de 61.
Ao escrever funções que atuam como métodos, algumas devem modificar o objeto (como player_take_damage), enquanto outras devem apenas ler dados sem alterar nada. A palavra-chave const permite que você aplique essa distinção no nível do compilador.
Ao marcar um parâmetro de ponteiro como const, você está prometendo que a função não modificará os dados para os quais ele aponta. Se você tentar alterar um membro acidentalmente, o compilador produzirá um erro:
typedef struct {
int x;
int y;
} Point;
// Esta função pode apenas LER o ponto
void point_print(const Point *self) {
printf("(%d, %d)\n", self->x, self->y);
// self->x = 10; // ERRO: não é possível modificar através de um ponteiro const
}
// Esta função pode MODIFICAR o ponto
void point_move(Point *self, int dx, int dy) {
self->x += dx;
self->y += dy;
}
O parâmetro const Point *self informa tanto ao compilador quanto a outros programadores que point_print é um "getter" ou uma operação de apenas leitura. Isso é chamado de const correctness—usar const sempre que a modificação não for necessária.
Essa prática detecta bugs precocemente. Se você escrever uma função display e acidentalmente modificar um campo, o compilador interrompe você imediatamente em vez de deixar o bug passar para o seu programa.
Desafio
FácilVamos construir um módulo Temperature que armazena um valor de temperatura e fornece operações de leitura e modificação. Isso ajudará você a praticar o uso de ponteiros const para distinguir claramente entre funções que apenas leem dados e aquelas que os alteram.
Você criará três arquivos:
temperature.h: Declare uma structTemperaturecom um único membrodouble celsius. Também declare três funções:temp_display— recebe um ponteiroconstpara Temperature e imprime a temperatura atual (somente leitura)temp_adjust— recebe um ponteiro comum para Temperature e um valor double, então adiciona o valor ao celsius (modificação)temp_to_fahrenheit— recebe um ponteiroconstpara Temperature e retorna o valor convertido para Fahrenheit (somente leitura)
TEMPERATURE_H.temperature.c: Implemente as três funções. A funçãotemp_displaydeve imprimir no formatoCurrent: %.1fC\n. A funçãotemp_to_fahrenheitdeve retornarcelsius * 9.0 / 5.0 + 32.0. Lembre-se de que funções que usam ponteirosconstnão podem modificar os dados da struct.main.c: Crie uma Temperature, realize operações e exiba os resultados.
Você receberá duas entradas: o valor inicial em celsius (como um double) e o valor de ajuste (como um double).
No seu arquivo principal:
- Inicialize uma
Temperaturecom o valor inicial em celsius - Chame
temp_displaypara mostrar a temperatura inicial - Chame
temp_adjustpara modificar a temperatura pelo valor de ajuste - Chame
temp_displaynovamente para mostrar a temperatura atualizada - Imprima a conversão para Fahrenheit usando
temp_to_fahrenheit
Imprima os resultados neste formato:
Current: {celsius}C
Current: {celsius}C
Fahrenheit: {fahrenheit}FUse %.1f para todos os valores de temperatura para exibir uma casa decimal.
Por exemplo, com um valor inicial de 20.0 e ajuste de 5.0, a saída seria:
Current: 20.0C
Current: 25.0C
Fahrenheit: 77.0FFolha de consulta
A palavra-chave const pode ser usada com parâmetros de ponteiro para indicar que uma função não modificará os dados que estão sendo apontados. Isso é chamado de const correctness.
Quando um parâmetro de ponteiro é marcado como const, o compilador impedirá quaisquer modificações nos dados através desse ponteiro:
typedef struct {
int x;
int y;
} Point;
// Função somente leitura - não pode modificar o ponto
void point_print(const Point *self) {
printf("(%d, %d)\n", self->x, self->y);
// self->x = 10; // ERRO: não é possível modificar através de um ponteiro const
}
// Função de modificação - pode alterar o ponto
void point_move(Point *self, int dx, int dy) {
self->x += dx;
self->y += dy;
}
Usar const Point *self comunica tanto ao compilador quanto a outros programadores que a função é uma operação de somente leitura. Isso ajuda a capturar bugs precocemente — se você acidentalmente tentar modificar dados em uma função que não deveria alterar nada, o compilador produzirá um erro imediatamente.
Melhor prática: Use const para parâmetros de ponteiro sempre que a função não precisar modificar os dados. Isso torna a intenção do seu código mais clara e evita modificações acidentais.
Experimente você mesmo
#include <stdio.h>
#include "temperature.h"
int main() {
double initial_celsius, adjustment;
scanf("%lf", &initial_celsius);
scanf("%lf", &adjustment);
// TODO: Crie uma struct Temperature e inicialize-a com initial_celsius
// TODO: Chame temp_display para mostrar a temperatura inicial
// TODO: Chame temp_adjust para modificar a temperatura pelo valor de adjustment
// TODO: Chame temp_display novamente para mostrar a temperatura atualizada
// TODO: Imprima a conversão para Fahrenheit usando temp_to_fahrenheit
// Formato: "Fahrenheit: %.1fF\n"
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1Fundamentos da Programação Modular
Arquivos de CabeçalhoInclude GuardsArquivos-FonteFunções EstáticasRecapitulação: Calculadora Modular4Encapsulamento
Conceito de Ponteiros OpacosDefinindo Structs OpacasGetters e SettersValidação em SettersRecapitulação: Caixa Secreta2Objetos e Métodos
Structs como ObjetosO Ponteiro 'Self'Const CorrectnessPonteiro vs ValorMétodos AuxiliaresRecapitulação: Point Manager5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta3Ciclo de Vida de Objetos
Padrão de ConstrutorPadrão de DestrutorInicialização na StackCópia ProfundaRecapitulação: String Wrapper6Herança via Composição
Incorporação de StructsA Regra do Primeiro MembroAcessando Membros PaiUpcastingRecapitulação: Hierarquia de Formas9Projeto: Desenhador de Formas
Visão Geral do ProjetoImplementação do Círculo