¿Qué son las macros en C? ¿Cómo puedo usar un libro para aprender más sobre ellos?

En pocas palabras, una macro tiene dos partes: un nombre y el “texto de reemplazo” correspondiente, de modo que cada vez que el preprocesador encuentra un nombre de macro, sustituye textualmente el nombre con el texto de reemplazo.

El ejemplo más simple de una macro sería:

#define SIZE_MAX 1024

Siempre que el preprocesador vea SIZE_MAX, se reemplazará con 1024.

Macros de funciones: las macros de funciones permiten que la macro tome un argumento para un reemplazo más sofisticado. Como ejemplo, considere una macro para un máximo de dos enteros:

#define max (a, b) ((a)> (b)? (a): (b))
int foo (int x, int y)
{
retorno max (a, b);
}

// Ejecutando esto a través de gcc -E muestra:
int f (int x, int y)
{
return ((a)> (b)? (a): (b));
}

Varias cosas a tener en cuenta sobre la macro anterior:

1] No hay espacio entre max y (en la definición de max. Intente poner el espacio y todo el significado cambia, con “nombre” como max y el resto como texto de reemplazo -;)

2] Lo que podría parecer un uso excesivo de los padres es un mecanismo de defensa (pobre) contra argumentos que son más complicados.

Por ejemplo:

#define mult (a, b) (a * b)

mult (4 + 5, 6 + 7) se expandiría a 4 + 5 * 6 + 7, lo que probablemente no fue el resultado esperado. Al paréntesis de los argumentos se expandiría a ((4) + (5)) * ((6) + (7)), que sería la intención más probable.

3] Las macros no pueden protegerse para todo tipo de expresiones.

Considere: mult (x ++, y ++) que se expandiría a ((x ++) + (y ++)) * ((x ++) + (y ++)) que es un comportamiento indefinido. Lamentablemente, en el estándar C no hay forma de protegerse contra esto. Sin embargo, gcc y clang (y posiblemente otros compiladores) ofrecen una extensión llamada expresión de declaración como solución alternativa.

4] Las macros no son de tipo seguro: la macro mult no está limitada a ningún tipo en particular y también podría usarse para otro tipo como double. mult (4.5, 1.2) se expandiría a 4.5 * 1.2. En general, las macros no forman parte del lenguaje C propiamente dicho y , como tal, pueden ser objeto de abuso, lo que da como resultado un código difícil de leer (y depurar). ¡El código fuente original de Bourne Shell es un excelente ejemplo de cómo NO usar macros! -;)

Casos prácticos de uso de macros:

Algunos de los casos de uso común de macro que conozco son:

1] Protector de encabezado:

Todo el archivo está envuelto

#ifndef FOO
#define FOO
// contenido del archivo de encabezado
#terminara si

La razón es evitar incluir un encabezado más de una vez, lo que puede ser problemático si el encabezado contiene definiciones. Por ejemplo, si una estructura se define en un archivo de encabezado y se incluye dos veces en el mismo archivo .c, el compilador se quejaría de la redefinición. Además, la mayoría de los compiladores de hoy en día omiten el procesamiento del archivo de encabezado cuando ven el protector de encabezado, esto se denomina optimización de inclusión múltiple.

2] Compilación condicional: un caso de uso de la compilación condicional es controlar varias dependencias de la plataforma en una base de código portátil.

Como ejemplo, una versión particular del compilador (y superior) puede proporcionar extensiones o atributos que podrían ayudarlo a optimizar o emitir mejores diagnósticos, pero al mismo tiempo, también queremos que el compilador del código sea compilado por otros compiladores que pueden no proporcionar el mismo extensiones

La siguiente función está tomada de la fuente de GCC en bitmap.h

vacío en línea estático
bmp_iter_next_bit (bitmap_iterator * bi, unsigned * bit_no)
{
#if (GCC_VERSION> = 3004)
{
unsigned int n = __builtin_ctzl (bi-> bits);
gcc_assert (sizeof (unsigned long) == sizeof (BITMAP_WORD));
bi-> bits >> = n;
* bit_no + = n;
}
#más
while (! (bi-> bits & 1))
{
bi-> bits >> = 1;
* bit_no + = 1;
}
#terminara si
}

Como se puede ver, gcc versión 3.4 y superior proporciona __builtin_ctzl y la función lo utiliza si compilando con gcc versión 3.4 o superior, de lo contrario, se vuelve a una solución más lenta.

Otro caso de uso es controlar la macro de aserción mediante NDEBUG. Si se define NDEBUG, la aserción no es una opción, el programa abortará en tiempo de ejecución si el argumento de aserción se evalúa a 0.

3] Xmacros: este es mi uso favorito de macro -;) El enlace explica el tema bastante bien, así que no lo repetiré aquí.

4] Pegado de tokens: el operador de pegado de tokens (##) combina ambos tokens adyacentes. Por ejemplo:

#define MANGLE (nombre) FOO_ ## nombre

MANGLE (x) daría como resultado FOO_x, que podría ser la solución alternativa de un hombre pobre para espacios de nombres en C. Vea el enlace para un ejemplo más práctico. También se puede usar el pegado de tokens para la programación genérica en C, lo cual es bastante interesante. Vea este blog para más detalles.

5] macro do-while (0): todavía se puede ver en algunas bases de código C (antiguas). No recomiendo usar esta técnica, prefiero las funciones en línea.

6] Macros Variadic

7] Selecciones genéricas: aunque no son directamente relevantes, las macros se pueden usar junto con la palabra clave _Generic (agregada en C11) para permitir una forma limitada de sobrecarga de funciones en C (sin usar una función variable).

Al concluir las observaciones, sugeriría utilizar macros como “último recurso” -;) Prefiero enumerar a #definir constantes y funciones en línea a funciones-macros.

Una macro es un nombre dado a un bloque de sentencias C como directiva de preprocesador.
Estas son macros predefinidas en lenguaje C
__DATE__String que contiene la fecha actual
__FILE__String que contiene el nombre del archivo
__LINE__Integer que representa el número de línea actual
__STDC__Si sigue el estándar ANSI C, entonces el valor es un entero distinto de cero
__TIME__String que contiene la fecha actual.
puedes referir esta url para
¿Cómo usar macros predefinidas?

Preprocesador de programación C y macros
¡Sigue así!