Las macros de preprocesador en C surgieron como parte del lenguaje para garantizar la portabilidad, legibilidad y facilidad de configuración del código fuente. Con las directivas #define, #ifdef, #ifndef, se pueden crear secciones condicionales de código, declarar constantes e incluir archivos externos (#include).
Historia de la cuestión:
Las directivas de preprocesador fueron introducidas para facilitar la adaptación del código fuente a diferentes sistemas y compiladores, así como para automatizar acciones repetitivas.
Problema:
Sin el preprocesador, no se podía abstraerse de las diferencias entre plataformas, repetir fragmentos de código y protegerse contra la inclusión múltiple de archivos de encabezado. Además, hay que tener cuidado, ya que las macros no están tipadas y no tienen alcance.
Solución:
Uso de macros de preprocesador para declarar constantes, funciones en línea, compilación condicional y prevenir la inclusión múltiple de archivos de encabezado.
Ejemplo de código:
#ifndef MY_HEADER_H #define MY_HEADER_H #define MAX_SIZE 100 #ifdef DEBUG #define LOG(x) printf("%s\n", x) #else #define LOG(x) #endif #endif /* MY_HEADER_H */
Características clave:
¿Qué sucederá si olvidamos el guard de inclusión en un archivo de encabezado?
Un archivo de encabezado puede ser incluido en un archivo .c varias veces (implícitamente a través de otros .h), lo que provocará errores de redefinición.
¿Cuál es la diferencia entre una macro #define y una declaración de función en línea?
#define simplemente reemplaza texto sin verificar tipos ni la corrección de la sintaxis, mientras que una función en línea es una función normal que es verificada por el compilador en la etapa de análisis de tipos.
¿Pueden las macros tener efectos secundarios?
Sí, si la macro está definida de manera descuidada, las expresiones pasadas pueden evaluarse varias veces. Por ejemplo:
#define SQUARE(x) (x) * (x) int a = SQUARE(++i); // ++i se ejecutará DOS VECES!
Uso de una macro sin paréntesis y con una expresión que crea efectos secundarios:
#define DOUBLE(x) x + x int result = DOUBLE(1+2); // ¿el resultado no es 6, sino 1+2+1+2=6? No, se convierte en 1+2+1+2=6 — ¿así? No, resulta en 1+2+1+2. // Pero en realidad será 1 + 2 + 1 + 2 = 6, pero si es DOUBLE(++i), entonces ++i se aplicará dos veces.
Pros:
Contras:
Definición de una macro con paréntesis y uso para constantes simples:
#define DOUBLE(x) ((x) + (x)) #define BUFFER_SIZE 1024
Pros:
Contras: