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.
- ¿Cuál es el mejor libro que aclarará los conceptos de Java para mí?
- ¿Los contenidos del libro de Balaguruswamy para C ++ y el libro de Sumita Arora para C ++ son los mismos?
- ¿Cuáles son los mejores libros que debe leer un desarrollador de Java?
- ¿De qué se trata Pyret?
- ¿Cuál es el mejor libro para aprender punteros en C?
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.