El preprocesador en C — ejercicios con el aforo de un ascensor, un limitador de plantas y un simulador con trazas (y el DEBUG 0 que no apagaba nada)
Después de aquel cero por entregar la práctica llena de trazas, me hice muy disciplinado: arriba del todo puse #define DEBUG 1 y todas las trazas dentro de un #ifdef DEBUG. Para la entrega, cambié el 1 por un 0, convencido de que así quedaban apagadas. Ejecuté el programa la noche antes de entregar y… ahí seguían todas, una detrás de otra. #ifdef no pregunta si vale 1 o 0: pregunta si existe. Y existía.
Estos ejercicios son para que a ti no te pase. Vas a escribir tus propias macros, a pelearte con las trampas del preprocesador y a montar un pequeño simulador con cabeceras y trazas que se encienden con -DDEBUG. Si te atascas, la teoría y la práctica tienen todo lo que hace falta.
Tabla de Contenidos
Cómo usar estos ejercicios
- Lee el enunciado y la salida esperada, y escribe tu solución sin mirar las soluciones.
- Compila siempre con
gcc -Wall -std=c11: en este tema los avisos del compilador te van a chivar más de un fallo de macro. - Si no sale, abre la 🔵 pista y, si sigue sin salir, mira tu código con
gcc -E -Ppara ver qué ha hecho el preprocesador. - Cuando funcione, compara con la solución: lo importante es entender por qué está hecha así, sobre todo los paréntesis.
Nivel 1: La calculadora de aforo
Escribe un programa con estas constantes: CARGA_MAXIMA 1500, PESO_ADULTO 75, PESO_NINO 35 y NUM_GRUPOS 4. Define dos macros:
PESO_GRUPO(adultos, ninos): el peso del grupo.CABE(kg): cierto si el ascensor aguanta esos kilos (1500 kg exactos todavía caben).
Los grupos tienen {4, 15, 20, 16} adultos y {2, 6, 0, 10} niños. Para cada grupo, imprime su peso, si cabe y, como prueba de fuego para tus paréntesis, el peso y el resultado si subiera un adulto más, calculado con PESO_GRUPO(adultos[i] + 1, ninos[i]).
Salida esperada:
Grupo 1 ( 4+ 2): 370 kg cabe | con un adulto más: 445 kg cabe Grupo 2 (15+ 6): 1335 kg cabe | con un adulto más: 1410 kg cabe Grupo 3 (20+ 0): 1500 kg cabe | con un adulto más: 1575 kg NO cabe Grupo 4 (16+10): 1550 kg NO cabe | con un adulto más: 1625 kg NO cabe
Si la columna «con un adulto más» te sale con cifras raras (por ejemplo, 149 en vez de 445 en el primer grupo), el + 1 se está sumando sin multiplicar. Revisa los paréntesis de los parámetros. Para alinear «cabe» y «NO cabe» usa %-9s, que rellena con espacios por la derecha hasta 9 caracteres.
Nivel 2: El limitador de plantas
El edificio va de la planta −2 a la 8, pero los botones del panel a veces mandan pedidos imposibles. Define las macros MIN(a, b), MAX(a, b), LIMITAR(x, lo, hi) (que deja x dentro del rango [lo, hi]) y ABS(x). La cabina empieza en la planta 0 y recibe los pedidos {5, 12, -4, 0, 8, -2, 9}. Para cada uno, la cabina va a la planta limitada; marca con «(corregido)» los que se han tenido que cambiar. Al final, cuenta los pedidos corregidos y las plantas recorridas en total.
Salida esperada:
Pedido 5 -> va a 5 Pedido 12 -> va a 8 (corregido) Pedido -4 -> va a -2 (corregido) Pedido 0 -> va a 0 Pedido 8 -> va a 8 Pedido -2 -> va a -2 Pedido 9 -> va a 8 (corregido) Corregidos: 3 | Plantas recorridas: 48
LIMITAR se puede escribir con las otras dos macros: primero se sube el valor al mínimo con MAX y luego se baja al máximo con MIN. Y no le pases pedidos[i++] a ninguna macro: copia antes el valor en una variable.
Nivel 3: El simulador del día
Ahora con cabeceras. Crea un ascensor.h, con guarda, que tenga CARGA_MAXIMA 1500, PESO_MEDIO 75, PERSONAS_MAX (calculada), las macros ABS, MIN y DIV_ARRIBA(a, b) (división entera redondeando hacia arriba) y un struct Grupo con origen, destino y personas. Crea también un registro.h, con guarda, que incluya ascensor.h y defina LOG(msg, valor): con -DDEBUG imprime [archivo:línea] msg = valor; sin él, no hace nada.
El programa simulador.c incluye los dos .h y simula estos cinco grupos: {0, 6, 8}, {0, 3, 25}, {4, -1, 12}, {7, 0, 41}, {-2, 5, 3}. Si un grupo no cabe de una vez, el ascensor hace varios viajes, y entre viaje y viaje vuelve vacío a por más gente. Para cada grupo, calcula los viajes y las plantas recorridas, con un LOG de cada valor; al final, los totales y el viaje más lleno del día.
Salida esperada sin -DDEBUG:
Grupo 1: 8 pers. 0 -> 6 | 1 viaje(s), 6 plantas Grupo 2: 25 pers. 0 -> 3 | 2 viaje(s), 9 plantas Grupo 3: 12 pers. 4 -> -1 | 1 viaje(s), 5 plantas Grupo 4: 41 pers. 7 -> 0 | 3 viaje(s), 35 plantas Grupo 5: 3 pers. -2 -> 5 | 1 viaje(s), 7 plantas Total: 8 viajes, 62 plantas, máximo 20 personas (1500 kg) a la vez
Y con gcc -Wall -std=c11 -DDEBUG simulador.c -o simulador:
[simulador.c:20] viajes = 1 [simulador.c:21] plantas = 6 Grupo 1: 8 pers. 0 -> 6 | 1 viaje(s), 6 plantas [simulador.c:20] viajes = 2 [simulador.c:21] plantas = 9 Grupo 2: 25 pers. 0 -> 3 | 2 viaje(s), 9 plantas [simulador.c:20] viajes = 1 [simulador.c:21] plantas = 5 Grupo 3: 12 pers. 4 -> -1 | 1 viaje(s), 5 plantas [simulador.c:20] viajes = 3 [simulador.c:21] plantas = 35 Grupo 4: 41 pers. 7 -> 0 | 3 viaje(s), 35 plantas [simulador.c:20] viajes = 1 [simulador.c:21] plantas = 7 Grupo 5: 3 pers. -2 -> 5 | 1 viaje(s), 7 plantas Total: 8 viajes, 62 plantas, máximo 20 personas (1500 kg) a la vez
DIV_ARRIBA(41, 20) tiene que dar 3. El truco clásico es sumar b - 1 antes de dividir: (41 + 19) / 20. Y si haces n viajes, el ascensor recorre el tramo 2n − 1 veces: n idas con gente y n − 1 vueltas vacías.
¿Qué falla aquí?
Tres fragmentos que compilan (alguno con avisos) y hacen algo distinto de lo que parece. Antes de leer la explicación, predice qué imprimen.
Fragmento 1: el ! que no niega
#include <stdio.h>
#define CARGA_MAXIMA 1500
#define DEMASIADO_GRANDE(v) v >= CARGA_MAXIMA
int main(void) {
int cargas[] = {300, 1650, 980};
for (int i = 0; i < 3; i++) {
if (!DEMASIADO_GRANDE(cargas[i]))
printf("%d kg: puede subir\n", cargas[i]);
else
printf("%d kg: demasiado peso\n", cargas[i]);
}
return 0;
}
¿Qué crees que sale para 300, 1650 y 980 kg?
Esto es lo que sale de verdad:
300 kg: demasiado peso 1650 kg: demasiado peso 980 kg: demasiado peso
Las tres cargas son «demasiado peso», hasta la de 300 kg. Y eso que gcc -Wall avisó al compilar (te enseño los avisos sin el número de columna):
fragmento.c: In function 'main': fragmento.c:4: warning: comparison of constant '1500' with boolean expression is always false [-Wbool-compare] fragmento.c:9: note: in expansion of macro 'DEMASIADO_GRANDE' fragmento.c:4: warning: logical not is only applied to the left hand side of comparison [-Wlogical-not-parentheses] fragmento.c:9: note: in expansion of macro 'DEMASIADO_GRANDE' fragmento.c:9: note: add parentheses around left hand side expression to silence this warning
El preprocesador convierte !DEMASIADO_GRANDE(cargas[i]) en !cargas[i] >= CARGA_MAXIMA, y el ! tiene más prioridad que >=. Así que C calcula (!cargas[i]) >= 1500: !300 vale 0 (cualquier número distinto de 0 negado da 0), y 0 >= 1500 es falso. El if nunca se cumple y todo va al else. El dibujo enseña los dos caminos:
Fiarse de que la macro «es una condición». Sin paréntesis exteriores, cualquier operador que pongas delante (un !, un -, un *) se pega solo al primer trozo. Los avisos -Wlogical-not-parentheses y -Wbool-compare te lo están diciendo.
El arreglo es poner la macro entera, y su parámetro, entre paréntesis:
#include <stdio.h>
#define CARGA_MAXIMA 1500
#define DEMASIADO_GRANDE(v) ((v) >= CARGA_MAXIMA)
int main(void) {
int cargas[] = {300, 1650, 980};
for (int i = 0; i < 3; i++) {
if (!DEMASIADO_GRANDE(cargas[i]))
printf("%d kg: puede subir\n", cargas[i]);
else
printf("%d kg: demasiado peso\n", cargas[i]);
}
return 0;
}
Y ahora sí:
300 kg: puede subir 1650 kg: demasiado peso 980 kg: puede subir
Ante un aviso que dice «in expansion of macro», el fallo casi nunca está en la línea del if, sino en el #define. Ve a la línea que indica el primer aviso (aquí, la 4).
Fragmento 2: el punto y coma que se cuela
#include <stdio.h>
#define CARGA_MAXIMA 1500;
int main(void) {
int carga = 640;
int libre = CARGA_MAXIMA - carga;
printf("Quedan %d kg libres\n", libre);
return 0;
}
Si hay 640 kg dentro, ¿cuántos quedan libres?
Lo que sale:
Quedan 1500 kg libres
1500 kg libres con 640 kg dentro. El aviso del compilador da la pista:
fragmento.c: In function 'main': fragmento.c:7: warning: statement with no effect [-Wunused-value]
El ; del #define es parte del texto de sustitución, así que la línea 7 queda como int libre = 1500; - carga;. Son dos sentencias: int libre = 1500; y - carga;, que calcula −640 y lo tira a la basura («statement with no effect»). libre se queda en 1500.
Terminar un #define con punto y coma por costumbre. Unas veces da un error de sintaxis y te enteras; otras, como aquí, compila y el resultado está mal.
El arreglo es quitar el ;:
#include <stdio.h>
#define CARGA_MAXIMA 1500
int main(void) {
int carga = 640;
int libre = CARGA_MAXIMA - carga;
printf("Quedan %d kg libres\n", libre);
return 0;
}
Ahora int libre = 1500 - carga; hace la cuenta entera:
Quedan 860 kg libres
1500 − 640 = 860.
Los avisos -Wunused-value («statement with no effect») en una línea que usa un #define casi siempre son un ; de más en la definición.
Fragmento 3: el DEBUG que vale 0
#include <stdio.h>
#define DEBUG 0 /* 0 = depuración apagada... ¿seguro? */
int main(void) {
int carga = 640;
#ifdef DEBUG
printf("[DEBUG] carga = %d\n", carga);
#endif
printf("Carga: %d kg\n", carga);
return 0;
}
Con DEBUG a 0, ¿sale la traza?
Sale:
[DEBUG] carga = 640 Carga: 640 kg
Es mi anécdota. #define DEBUG 0 define el símbolo DEBUG, aunque su valor sea 0. Y #ifdef DEBUG solo pregunta si existe, así que la traza pasa al compilador. Aquí ni siquiera hay aviso: para el preprocesador está todo correcto.
Usar #ifdef con un símbolo que tiene valor 0 o 1. O lo defines y lo quitas (#ifdef + -DDEBUG), o le das valor y lo comparas (#if DEBUG). Mezclar las dos ideas no funciona.
El arreglo, si quieres mantener el 0 y el 1, es preguntar por el valor con #if:
#include <stdio.h>
#define DEBUG 0 /* 0 = apagada, 1 = encendida */
int main(void) {
int carga = 640;
#if DEBUG
printf("[DEBUG] carga = %d\n", carga);
#endif
printf("Carga: %d kg\n", carga);
return 0;
}
Y la traza desaparece:
Carga: 640 kg
Con #if DEBUG, si un día borras el #define DEBUG el preprocesador lo trata como 0 y las trazas se apagan solas. Es la versión más segura de las dos.
Soluciones
Solución del Nivel 1
#include <stdio.h>
#define CARGA_MAXIMA 1500
#define PESO_ADULTO 75
#define PESO_NINO 35
#define NUM_GRUPOS 4
/* Paréntesis en cada parámetro: así PESO_GRUPO(a + 1, n) sigue funcionando */
#define PESO_GRUPO(adultos, ninos) ((adultos) * PESO_ADULTO + (ninos) * PESO_NINO)
#define CABE(kg) ((kg) <= CARGA_MAXIMA) /* <= : justo 1500 kg todavía cabe */
int main(void) {
int adultos[NUM_GRUPOS] = {4, 15, 20, 16};
int ninos[NUM_GRUPOS] = {2, 6, 0, 10};
for (int i = 0; i < NUM_GRUPOS; i++) {
int kg = PESO_GRUPO(adultos[i], ninos[i]);
int con_uno_mas = PESO_GRUPO(adultos[i] + 1, ninos[i]); /* prueba de fuego */
printf("Grupo %d (%2d+%2d): %4d kg %-9s | con un adulto más: %4d kg %s\n",
i + 1, adultos[i], ninos[i], kg, CABE(kg) ? "cabe" : "NO cabe",
con_uno_mas, CABE(con_uno_mas) ? "cabe" : "NO cabe");
}
return 0;
}
Por qué está hecho así:
PESO_GRUPOlleva paréntesis en(adultos)y en(ninos). ConPESO_GRUPO(adultos[i] + 1, ninos[i]), sin ellos quedaríaadultos[i] + 1 * 75 + ninos[i] * 35: solo el adulto extra se multiplicaría por 75 y el primer grupo pesaría 4 + 75 + 70 = 149 kg en vez de 445. La columna de la derecha es la que lo comprueba.- La expresión entera va entre paréntesis por si alguien escribe
2 * PESO_GRUPO(...). CABEusa<=porque 1500 kg exactos caben (el grupo 3 lo comprueba: 20 · 75 = 1500 → «cabe»).CABE(kg) ? "cabe" : "NO cabe"elige el texto con el operador ternario, y%-9slo alinea.
Las cuentas, comprobadas:
| Grupo | Peso | ¿Cabe? | Con un adulto más | ¿Cabe? |
|---|---|---|---|---|
| 1 | 4 · 75 + 2 · 35 = 300 + 70 = 370 | Sí | 370 + 75 = 445 | Sí |
| 2 | 15 · 75 + 6 · 35 = 1125 + 210 = 1335 | Sí | 1410 | Sí |
| 3 | 20 · 75 + 0 = 1500 | Sí (justo) | 1575 | No |
| 4 | 16 · 75 + 10 · 35 = 1200 + 350 = 1550 | No | 1625 | No |
Solución del Nivel 2
#include <stdio.h>
#define PLANTA_MIN (-2)
#define PLANTA_MAX 8
#define NUM_PEDIDOS 7
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define LIMITAR(x, lo, hi) MIN(MAX((x), (lo)), (hi)) /* primero el suelo, luego el techo */
#define ABS(x) ((x) < 0 ? -(x) : (x))
int main(void) {
int pedidos[NUM_PEDIDOS] = {5, 12, -4, 0, 8, -2, 9};
int actual = 0, corregidos = 0, recorrido = 0;
for (int i = 0; i < NUM_PEDIDOS; i++) {
int pedido = pedidos[i]; /* copia: nada de pedidos[i++] dentro de la macro */
int planta = LIMITAR(pedido, PLANTA_MIN, PLANTA_MAX);
if (planta != pedido)
corregidos++;
recorrido += ABS(planta - actual); /* plantas que se mueve la cabina */
printf("Pedido %3d -> va a %2d%s\n", pedido, planta,
planta != pedido ? " (corregido)" : "");
actual = planta;
}
printf("Corregidos: %d | Plantas recorridas: %d\n", corregidos, recorrido);
return 0;
}
Por qué está hecho así:
LIMITAR(x, lo, hi)esMIN(MAX((x), (lo)), (hi)):MAXsube cualquier pedido por debajo de −2 hasta −2 yMINbaja cualquiera por encima de 8 hasta 8. Las macros se pueden anidar: el preprocesador vuelve a examinar el texto que sale de una sustitución.PLANTA_MINes(-2)entre paréntesis por la regla de siempre: es una expresión, no un número suelto.int pedido = pedidos[i];es la decisión clave.LIMITARacaba repitiendoxvarias veces al expandirse (tienesMAXdentro deMIN, y cada una repite sus parámetros), así que conLIMITAR(pedidos[i++], …)el índice avanzaría varias veces en una sola vuelta.recorrido += ABS(planta - actual)suma la distancia en plantas, sin signo: bajar de 8 a −2 son 10 plantas, igual que subir.
La traza vuelta a vuelta:
| Vuelta | pedido | planta | ¿Corregido? | ABS(planta - actual) | recorrido |
|---|---|---|---|---|---|
| 0 | 5 | 5 | No | |5 − 0| = 5 | 5 |
| 1 | 12 | 8 | Sí | |8 − 5| = 3 | 8 |
| 2 | −4 | −2 | Sí | |−2 − 8| = 10 | 18 |
| 3 | 0 | 0 | No | |0 − (−2)| = 2 | 20 |
| 4 | 8 | 8 | No | |8 − 0| = 8 | 28 |
| 5 | −2 | −2 | No | |−2 − 8| = 10 | 38 |
| 6 | 9 | 8 | Sí | |8 − (−2)| = 10 | 48 |
Tres corregidos y 48 plantas, como en la salida.
Solución del Nivel 3
Primero, ascensor.h:
/* ascensor.h: constantes, macros y el struct del simulador */
#ifndef ASCENSOR_H
#define ASCENSOR_H
#define CARGA_MAXIMA 1500
#define PESO_MEDIO 75
#define PERSONAS_MAX (CARGA_MAXIMA / PESO_MEDIO) /* 20 */
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define DIV_ARRIBA(a, b) (((a) + (b) - 1) / (b)) /* división redondeando hacia arriba */
struct Grupo {
int origen;
int destino;
int personas;
};
#endif /* ASCENSOR_H */
Luego, registro.h:
/* registro.h: trazas que solo existen al compilar con -DDEBUG */
#ifndef REGISTRO_H
#define REGISTRO_H
#include "ascensor.h" /* se incluye otra vez desde el .c: la guarda lo evita */
#ifdef DEBUG
#define LOG(msg, valor) printf(" [%s:%d] %s = %d\n", __FILE__, __LINE__, msg, valor)
#else
#define LOG(msg, valor) ((void)0)
#endif
#endif /* REGISTRO_H */
Y el programa:
#include <stdio.h>
#include "ascensor.h"
#include "registro.h"
#define NUM_GRUPOS 5
int main(void) {
struct Grupo dia[NUM_GRUPOS] = {
{0, 6, 8}, {0, 3, 25}, {4, -1, 12}, {7, 0, 41}, {-2, 5, 3}
};
int total_viajes = 0, total_plantas = 0, mas_lleno = 0;
for (int i = 0; i < NUM_GRUPOS; i++) {
struct Grupo g = dia[i];
int viajes = DIV_ARRIBA(g.personas, PERSONAS_MAX);
int tramo = ABS(g.destino - g.origen);
/* ida, vuelta vacía a por más gente, ida...: 2 * viajes - 1 tramos */
int plantas = tramo * (2 * viajes - 1);
int en_cabina = MIN(g.personas, PERSONAS_MAX); /* el viaje más lleno del grupo */
LOG("viajes", viajes);
LOG("plantas", plantas);
printf("Grupo %d: %2d pers. %2d -> %2d | %d viaje(s), %2d plantas\n",
i + 1, g.personas, g.origen, g.destino, viajes, plantas);
total_viajes += viajes;
total_plantas += plantas;
if (en_cabina > mas_lleno)
mas_lleno = en_cabina;
}
printf("Total: %d viajes, %d plantas, máximo %d personas (%d kg) a la vez\n",
total_viajes, total_plantas, mas_lleno, mas_lleno * PESO_MEDIO);
return 0;
}
Por qué está hecho así:
- Las dos guardas (
ASCENSOR_HyREGISTRO_H) son imprescindibles:simulador.cincluyeascensor.hdirectamente y otra vez a través deregistro.h. Sin la guarda deascensor.h,struct Grupoquedaría definido dos veces y no compilaría. - **
DIV_ARRIBA(a, b)** es(((a) + (b) - 1) / (b)). Con la división entera normal, 41 / 20 = 2 y una persona se quedaría en la planta 7; sumando 19 antes, (41 + 19) / 20 = 60 / 20 = 3. Si ya era exacto, no cambia nada: (40 + 19) / 20 = 59 / 20 = 2. - **
plantas = tramo * (2 * viajes - 1)**: con un viaje, el tramo una vez; con tres, ida, vuelta, ida, vuelta, ida = cinco veces. - **
MIN(g.personas, PERSONAS_MAX)** es la gente que va en el viaje más lleno de ese grupo: si son 8, van los 8; si son 41, como mucho 20. - **
LOG** sale deregistro.h, así que el.cno sabe ni le importa si las trazas están encendidas.__LINE__dice 20 y 21, las líneas desimulador.cdonde se usa, aunque la macro esté escrita en otro archivo.
Las cuentas de cada grupo:
| Grupo | Personas | viajes | tramo | plantas | En el viaje más lleno |
|---|---|---|---|---|---|
| 1 | 8 | (8 + 19) / 20 = 1 | |6 − 0| = 6 | 6 · 1 = 6 | 8 |
| 2 | 25 | (25 + 19) / 20 = 2 | 3 | 3 · 3 = 9 | 20 |
| 3 | 12 | 1 | |−1 − 4| = 5 | 5 | 12 |
| 4 | 41 | 3 | 7 | 7 · 5 = 35 | 20 |
| 5 | 3 | 1 | |5 − (−2)| = 7 | 7 | 3 |
Total: 1 + 2 + 1 + 3 + 1 = 8 viajes y 6 + 9 + 5 + 35 + 7 = 62 plantas; el viaje más lleno lleva 20 personas, 20 · 75 = 1500 kg. El grupo 4 es el más interesante, y el dibujo lo enseña entero:
Míralo paso a paso en Python Tutor
Python Tutor ejecuta el código línea a línea y dibuja las variables. Elige C y pega esta versión del fragmento 1, que guarda cada paso en una variable para que veas el fallo en directo:
#include <stdio.h>
#define CARGA_MAXIMA 1500
#define DEMASIADO_GRANDE(v) v >= CARGA_MAXIMA /* sin paréntesis */
int main(void) {
int carga = 300;
int negado = !carga; /* lo que calcula de verdad el ! */
int resultado = !DEMASIADO_GRANDE(carga); /* !carga >= 1500 */
int bien = !(carga >= CARGA_MAXIMA); /* lo que queríamos */
printf("negado = %d, resultado = %d, bien = %d\n", negado, resultado, bien);
return 0;
}
Su salida:
negado = 0, resultado = 0, bien = 1
Qué observar paso a paso:
- **
cargavale 300** y es lo único que existe al empezar; niCARGA_MAXIMAni la macro aparecen como variables. - **Al crearse
negado**, vale 0:!300es 0, porque cualquier número distinto de cero cuenta como «verdadero» y negarlo da «falso». - **Al crearse
resultado**, también vale 0. Es la macro sin paréntesis:!carga >= 1500esnegado >= 1500, es decir,0 >= 1500. - **Al crearse
bien**, vale 1: con los paréntesis bien puestos,300 >= 1500es 0 y negarlo da 1. Esa es la diferencia entre «demasiado peso» y «puede subir».
Chuletario: el preprocesador en C
Lo esencial en 30 segundos: el preprocesador trabaja antes del compilador y solo con texto. #include pega archivos, #define sustituye símbolos (y macros con parámetros), #ifdef/#if dejan o quitan código, y gcc -D define símbolos desde fuera. En las macros, paréntesis en cada parámetro y alrededor de todo; nunca ; al final ni argumentos con efectos.
Directivas:
| Quiero… | Código | Resultado |
|---|---|---|
| Usar una biblioteca del sistema | #include <stdio.h> | Se pega stdio.h |
| Usar mi cabecera | #include "ascensor.h" | Se busca en mi carpeta |
| Una constante | #define CARGA_MAXIMA 1500 | CARGA_MAXIMA → 1500 |
| Un símbolo sin valor | #define CON_PANTALLA | Solo existe |
| Una macro | #define PESO(n) ((n) * 75) | PESO(a + b) → ((a + b) * 75) |
| Borrar un símbolo | #undef CON_PANTALLA | Deja de existir |
| Una macro de varias líneas | \ al final de cada línea | Una sola definición |
Compilación condicional:
| Quiero… | Código | Resultado |
|---|---|---|
Trazas solo con -DDEBUG | #ifdef DEBUG … #endif | Sin -DDEBUG, desaparecen |
| Valor por defecto cambiable | #ifndef N / #define N 10 / #endif | -DN=20 lo cambia |
| Elegir versión por número | #if MODELO == 1 … #elif … #else … #endif | Solo queda una rama |
| Apagar un bloque | #if 0 … #endif | No llega al compilador |
| Impedir una compilación | #error "mensaje" | gcc para con ese mensaje |
| Definir desde la terminal | gcc -DDEBUG / gcc -DMODELO=2 | Como un #define en la línea 1 |
Plantillas de los ejercicios:
| Plantilla | Código | Resultado |
|---|---|---|
| Peso de un grupo | #define PESO_GRUPO(a, n) ((a) * 75 + (n) * 35) | PESO_GRUPO(4, 2) → 370 |
| Cabe o no | #define CABE(kg) ((kg) <= CARGA_MAXIMA) | CABE(1500) → 1 |
| Mínimo y máximo | #define MIN(a, b) ((a) < (b) ? (a) : (b)) | MIN(12, 8) → 8 |
| Limitar a un rango | #define LIMITAR(x, lo, hi) MIN(MAX((x), (lo)), (hi)) | LIMITAR(-4, -2, 8) → −2 |
| Valor absoluto | #define ABS(x) ((x) < 0 ? -(x) : (x)) | ABS(-10) → 10 |
| División hacia arriba | #define DIV_ARRIBA(a, b) (((a) + (b) - 1) / (b)) | DIV_ARRIBA(41, 20) → 3 |
| Traza con ubicación | printf("[%s:%d] ...", __FILE__, __LINE__, ...) | [simulador.c:20] ... |
Guarda de un .h | #ifndef ASCENSOR_H / #define ASCENSOR_H / … / #endif | Se incluye una vez |
Síntoma / causa probable:
| Síntoma | Causa probable |
|---|---|
| Una cuenta con una macro da un número raro | Faltan paréntesis en un parámetro o alrededor de todo |
Un !MACRO(x) nunca (o siempre) se cumple | Falta el paréntesis exterior de la macro |
statement with no effect en una línea con un #define | ; al final del #define |
| Una variable avanza dos veces | Has pasado i++ a una macro que repite el parámetro |
Las trazas salen aunque DEBUG vale 0 | Usas #ifdef DEBUG en vez de #if DEBUG |
redefinition of 'struct …' | Un .h sin guarda incluido dos veces |
Muchos undeclared seguidos | Mira el primer error: un #error o un #include que falla |
Antes de entregar, pregúntate:
- ¿Todas mis macros tienen paréntesis en cada parámetro y alrededor de la expresión?
- ¿Algún
#definetermina en;? - ¿Mis trazas desaparecen al compilar sin
-DDEBUG(y no hay efectos dentro de ellas)? - ¿Todos mis
.htienen su guarda#ifndef … #define … #endif?
¿Te ha servido este artículo? Así puedes apoyar SergioLearns →
