Menu

Constantes en C: const, #define y enum

Tres formas de nombrar un valor que nunca cambia en C — la palabra clave const, las macros #define y las constantes de enum — qué es realmente cada una y cuál conviene usar en cada caso.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Una constante es un valor con un nombre y la promesa de que no va a cambiar. C te da tres mecanismos para eso, y por dentro son cosas genuinamente distintas: uno es una variable, otro es sustitución de texto y otro es un tipo entero.

Por qué nombrar un valor

Compara estas dos líneas:

double area = 3.14159 * r * r;
double area = PI * r * r;

La segunda es mejor por tres razones que aparecen todas más adelante. Dice qué significa el número. Aparece en exactamente un lugar, así que cambiar la precisión es una sola edición en vez de una búsqueda. Y un error de tipeo en PI es un error de compilación, mientras que un error de tipeo en 3.14195 es una respuesta equivocada.

A los números sin nombre esparcidos por el código se les llama números mágicos, y eliminarlos es una de las mejoras más baratas que puedes hacerle a un programa en C.

const

La palabra clave const marca una variable como de solo lectura:

Descomenta la asignación y el compilador se niega: assignment of read-only variable 'PI'. Ese error en tiempo de construcción es justamente el objetivo.

Una variable const es una variable real. Tiene un tipo, así que el compilador verifica cómo la usas. Respeta el alcance, así que una const dentro de una función no se escapa. Tiene una dirección, así que puedes tomar un puntero a ella. Y un depurador puede mostrarte su nombre y su valor.

Una const debe inicializarse en el punto de la declaración: no hay una segunda oportunidad.

const int limit;       /* error (o basura para siempre): nada puede asignarle un valor después */
const int limit = 50;  /* correcto */

#define

La alternativa del preprocesador es una macro:

Esto no es una variable. Antes de que corra el compilador, el preprocesador reemplaza cada aparición de PI en el código fuente por los caracteres 3.14159265358979. El compilador nunca ve el nombre: por eso exactamente un #define no tiene tipo y no puede aparecer en un depurador.

Fíjate que no hay punto y coma ni =. Una macro es texto crudo, así que este error común:

#define MAX_USERS 100;        /* nota el punto y coma */

int limit = MAX_USERS + 1;    /* se expande a: int limit = 100; + 1;  -> error */

produce un error en una línea que no se parece en nada a la definición. La convención de nombrar las macros en mayúsculas existe precisamente para que quien lee sepa que está ocurriendo una sustitución.

Las macros se escriben por convención en MAYUSCULAS; las variables const suelen seguir la misma regla cuando representan la misma idea.

Constantes de enum

La tercera forma te da un conjunto de enteros con nombre:

Los valores empiezan en 0 y aumentan de uno en uno a menos que los asignes, así que MON es 0 y FRI es 4. Un enum { ... } anónimo es un modismo común para constantes enteras simples.

Las constantes de enum tienen una ventaja real sobre ambas alternativas: son constantes enteras de tiempo de compilación que el compilador entiende, y son visibles para el depurador. La historia completa está en enums en C.

Cuál usar

NecesidadUsa
Una constante con tipo, alcance y depurableconst
Un tamaño de array, una etiqueta case, el ancho de un campo de bitsenum o #define
Una constante de cadena#define o const char *
Una constante de punto flotanteconst double
Un conjunto de valores enteros relacionadosenum
Cualquier cosa que el preprocesador deba ver (#if, guards de inclusión)#define

El valor por defecto debería ser const. Recurre a #define cuando el valor tenga que existir antes de la compilación, y a enum cuando necesites un entero de tiempo de compilación o un grupo con nombre.

El lugar donde esto realmente muerde es en los tamaños de arrays:

Un const int en C es una variable de solo lectura, no una expresión constante de tiempo de compilación. Dentro de una función, C99 acepta int local[const_size] convirtiéndolo en un array de longitud variable; a nivel de archivo, dentro de un struct o como etiqueta case se rechaza de plano. enum y #define funcionan en todas partes.

(Esta es una de las diferencias reales con C++, donde const int sí es una expresión constante. El código que se mueve entre los dos lenguajes tropieza con esto.)

const con punteros

const y los punteros se combinan de tres maneras, y los significados no son intercambiables. Lee la declaración desde el nombre hacia afuera:

const char *p;          /* p apunta a chars que no deben modificarse */
char * const q;         /* q mismo no puede reapuntarse; los chars sí pueden cambiar */
const char * const r;   /* ni el puntero ni los chars pueden cambiar */

La forma const char * es la que más vas a escribir, porque es el tipo correcto para un parámetro de función que lee una cadena sin cambiarla:

Agregar const a un parámetro de solo lectura no cuesta nada y documenta el contrato de una forma que el compilador hace cumplir. Vale la pena hacerlo en todas partes.

Los literales de cadena son efectivamente const

Una trampa sutil que const existe para atrapar:

char *s = "hola";    /* C legal, pero s apunta a memoria de solo lectura */
s[0] = 'H';          /* comportamiento indefinido: normalmente un crash */

Los literales de cadena viven en una sección de solo lectura del ejecutable. Escribir a través de ese puntero típicamente provoca un segfault. Declarar el puntero como const char *s = "hola"; hace que el compilador rechace la asignación en lugar de dejar que falle en tiempo de ejecución.

Si necesitas una cadena modificable, cópiala a un array:

Preguntas frecuentes

¿Cómo se declara una constante en C?

Agrega const a una declaración y dale un valor: const double PI = 3.14159;. El compilador rechaza entonces cualquier intento de asignarle algo. La alternativa más antigua es una macro del preprocesador, #define PI 3.14159, que en cambio hace una sustitución de texto.

¿Cuál es la diferencia entre const y #define en C?

const crea una variable real con tipo que el compilador conoce, así que verifica tipos, aparece en el depurador y respeta el alcance. #define es puro reemplazo de texto hecho antes de la compilación: no tiene tipo, no tiene alcance y no se puede inspeccionar mientras depuras. Prefiere const a menos que necesites el valor en un contexto que requiera una constante en tiempo de compilación.

¿Se puede usar una variable const como tamaño de un array en C?

No para un array de tamaño fijo a nivel de archivo. const int N = 10; int arr[N]; es un array de longitud variable en C99 (permitido dentro de una función, no a nivel de archivo, y no dentro de un struct). Para un tamaño genuino en tiempo de compilación, usa #define N 10 o un enum { N = 10 };.

¿Qué significa const char *p?

Es un puntero a un char constante: puedes cambiar a dónde apunta p, pero no el carácter al que apunta. char * const p es lo contrario: un puntero constante a un char modificable. const char * const p bloquea ambos. Lee la declaración desde el nombre hacia afuera.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR