Const Correctness
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 8 de 61.
Al escribir funciones que actúan como métodos, algunas deberían modificar el objeto (como player_take_damage), mientras que otras solo deberían leer datos sin cambiar nada. La palabra clave const te permite aplicar esta distinción a nivel del compilador.
Al marcar un parámetro de puntero como const, estás prometiendo que la función no modificará los datos a los que apunta. Si intentas cambiar un miembro accidentalmente, el compilador producirá un error:
typedef struct {
int x;
int y;
} Point;
// Esta función solo puede LEER el punto
void point_print(const Point *self) {
printf("(%d, %d)\n", self->x, self->y);
// self->x = 10; // ERROR: no se puede modificar a través de un puntero const
}
// Esta función puede MODIFICAR el punto
void point_move(Point *self, int dx, int dy) {
self->x += dx;
self->y += dy;
}
El parámetro const Point *self indica tanto al compilador como a otros programadores que point_print es un "getter" o una operación de solo lectura. Esto se llama const correctness: el uso de const siempre que no sea necesaria una modificación.
Esta práctica detecta errores de forma temprana. Si escribes una función display y accidentalmente modificas un campo, el compilador te detiene inmediatamente en lugar de permitir que el error se cuele en tu programa.
Desafío
FácilVamos a construir un módulo Temperature que almacena un valor de temperatura y proporciona operaciones tanto de solo lectura como de modificación. Esto te ayudará a practicar el uso de punteros const para distinguir claramente entre las funciones que solo leen datos y aquellas que los cambian.
Crearás tres archivos:
temperature.h: Declara una estructuraTemperaturecon un único miembrodouble celsius. También declara tres funciones:temp_display— recibe un punteroconsta Temperature e imprime la temperatura actual (solo lectura)temp_adjust— recibe un puntero normal a Temperature y un valor double amount, luego suma esa cantidad a celsius (modificación)temp_to_fahrenheit— recibe un punteroconsta Temperature y devuelve el valor convertido a Fahrenheit (solo lectura)
TEMPERATURE_H.temperature.c: Implementa las tres funciones. La funcióntemp_displaydebe imprimir en el formato"Current: %.1fC\n". La funcióntemp_to_fahrenheitdebe devolvercelsius * 9.0 / 5.0 + 32.0. Recuerda que las funciones que usan punterosconstno pueden modificar los datos de la estructura.main.c: Crea una Temperature, realiza operaciones y muestra los resultados.
Recibirás dos entradas: el valor inicial en celsius (como un double) y la cantidad de ajuste (como un double).
En tu archivo principal:
- Inicializa una
Temperaturecon el valor inicial en celsius - Llama a
temp_displaypara mostrar la temperatura inicial - Llama a
temp_adjustpara modificar la temperatura según la cantidad de ajuste - Llama a
temp_displayde nuevo para mostrar la temperatura actualizada - Imprime la conversión a Fahrenheit usando
temp_to_fahrenheit
Imprime los resultados en este formato:
Current: {celsius}C
Current: {celsius}C
Fahrenheit: {fahrenheit}FUsa %.1f para todos los valores de temperatura para mostrar un decimal.
Por ejemplo, con un valor inicial de 20.0 y un ajuste de 5.0, la salida sería:
Current: 20.0C
Current: 25.0C
Fahrenheit: 77.0FHoja de referencia
La palabra clave const puede usarse con parámetros de puntero para indicar que una función no modificará los datos a los que se apunta. Esto se llama const correctness.
Cuando un parámetro de puntero se marca como const, el compilador evitará cualquier modificación de los datos a través de ese puntero:
typedef struct {
int x;
int y;
} Point;
// Función de solo lectura - no puede modificar el punto
void point_print(const Point *self) {
printf("(%d, %d)\n", self->x, self->y);
// self->x = 10; // ERROR: no se puede modificar a través de un puntero const
}
// Función de modificación - puede cambiar el punto
void point_move(Point *self, int dx, int dy) {
self->x += dx;
self->y += dy;
}
El uso de const Point *self comunica tanto al compilador como a otros programadores que la función es una operación de solo lectura. Esto ayuda a detectar errores a tiempo: si accidentalmente intentas modificar datos en una función que no debería cambiar nada, el compilador producirá un error de inmediato.
Mejor práctica: Usa const para parámetros de puntero siempre que la función no necesite modificar los datos. Esto hace que la intención de tu código sea más clara y evita modificaciones accidentales.
Pruébalo tú mismo
#include <stdio.h>
#include "temperature.h"
int main() {
double initial_celsius, adjustment;
scanf("%lf", &initial_celsius);
scanf("%lf", &adjustment);
// TODO: Crear una estructura Temperature e inicializarla con initial_celsius
// TODO: Llamar a temp_display para mostrar la temperatura inicial
// TODO: Llamar a temp_adjust para modificar la temperatura por la cantidad de ajuste
// TODO: Llamar a temp_display de nuevo para mostrar la temperatura actualizada
// TODO: Imprimir la conversión a Fahrenheit usando temp_to_fahrenheit
// Formato: "Fahrenheit: %.1fF\n"
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de programación modular
Archivos de cabeceraGuardas de inclusiónArchivos fuenteFunciones estáticasRepaso: Calculadora modular4Encapsulamiento
Concepto de punteros opacosDefinición de structs opacosGetters y SettersValidación en settersResumen: Caja secreta7Punteros a funciones
Declaración de punteros a funcionesLlamada a punteros a funcionesTypedef para punteros a funcionesPasar funciones como argumentosRepaso: Dispatch de calculadora10Contenedores genéricos
Repaso de punteros voidWrapper genéricoSwap genéricoComparación genéricaRepaso: Array genérico2Objetos y métodos
Structs como objetosEl puntero 'Self'Const CorrectnessPuntero vs. ValorMétodos auxiliaresResumen: Point Manager5Proyecto: Cuenta bancaria simple
Configuración del proyectoImplementación de la cuenta3Ciclo de vida de los objetos
Patrón ConstructorPatrón DestructorInicialización en StackCopia profundaResumen: String Wrapper6Herencia mediante composición
Incrustación de StructsLa regla del primer miembroAcceso a miembros padreUpcastingResumen: Jerarquía de formas9Proyecto: Dibujador de figuras
Descripción general del proyectoImplementación del círculo