Siempre vi ejemplos y casos en los que usar una macro es mejor que usar una función.
¿Alguien podría explicarme con un ejemplo la desventaja de una macro en comparación con una función?
c
function
c-preprocessor
Kyrol
fuente
fuente
Respuestas:
Las macros son propensas a errores porque se basan en la sustitución textual y no realizan verificación de tipos. Por ejemplo, esta macro:
funciona bien cuando se usa con un número entero:
pero hace cosas muy extrañas cuando se usa con expresiones:
Poner paréntesis alrededor de los argumentos ayuda, pero no elimina por completo estos problemas.
Cuando las macros contienen varias declaraciones, puede tener problemas con las construcciones de flujo de control:
La estrategia habitual para solucionar este problema es colocar las declaraciones dentro de un bucle "do {...} while (0)".
Si tiene dos estructuras que contienen un campo con el mismo nombre pero semántica diferente, la misma macro podría funcionar en ambas, con resultados extraños:
Finalmente, las macros pueden ser difíciles de depurar, produciendo extraños errores de sintaxis o errores de tiempo de ejecución que tienes que expandir para entenderlos (por ejemplo, con gcc -E), porque los depuradores no pueden recorrer las macros, como en este ejemplo:
Las funciones y constantes en línea ayudan a evitar muchos de estos problemas con las macros, pero no siempre son aplicables. Cuando las macros se utilizan deliberadamente para especificar el comportamiento polimórfico, el polimorfismo involuntario puede ser difícil de evitar. C ++ tiene una serie de características, como plantillas, para ayudar a crear construcciones polimórficas complejas de forma segura sin el uso de macros; consulte El lenguaje de programación C ++ de Stroustrup para obtener más detalles.
fuente
x++*x++
no se puede decir que la expresión se incrementex
dos veces; de hecho, invoca un comportamiento indefinido , lo que significa que el compilador es libre de hacer lo que quiera: podría incrementarx
dos veces, una vez, o nada en absoluto; podría abortar con un error o incluso hacer que los demonios salgan volando de tu nariz .Funciones macro :
Características de la función :
fuente
Los efectos secundarios son importantes. Este es un caso típico:
se expande a:
x
se incrementa dos veces en la misma declaración. (y comportamiento indefinido)Escribir macros de varias líneas también es un problema:
Requieren un
\
al final de cada línea.Las macros no pueden "devolver" nada a menos que lo convierta en una sola expresión:
No se puede hacer eso en una macro a menos que use la declaración de expresión de GCC. (EDITAR: aunque puede usar un operador de coma ... lo pasó por alto ... pero aún podría ser menos legible).
Orden de operaciones: (cortesía de @ouah)
se expande a:
Pero
&
tiene menor precedencia que<
. Entonces0xFF < 42
se evalúa primero.fuente
min(a & 0xFF, 42)
Ejemplo 1:
mientras:
Ejemplo 2:
Comparado con:
fuente
En caso de duda, utilice funciones (o funciones en línea).
Sin embargo, las respuestas aquí explican principalmente los problemas con las macros, en lugar de tener una visión simple de que las macros son malas porque los accidentes tontos son posibles.
Puede ser consciente de los peligros y aprender a evitarlos. Luego, use macros solo cuando haya una buena razón para hacerlo.
Hay ciertos casos excepcionales en los que el uso de macros tiene ventajas, entre los que se incluyen:
va_args
.por ejemplo: https://stackoverflow.com/a/24837037/432509 .
(
__FILE__
,__LINE__
,__func__
). verifique las condiciones previas / posteriores,assert
en caso de falla o incluso afirmaciones estáticas para que el código no se compile en caso de uso incorrecto (principalmente útil para compilaciones de depuración).struct
miembros estén presentes antes de lanzar(puede ser útil para tipos polimórficos) .
O verifique que una matriz cumpla con alguna condición de longitud.
ver: https://stackoverflow.com/a/29926435/432509
func(FOO, "FOO");
, podría definir una macro que expanda la cadena por ustedfunc_wrapper(FOO);
(asignaciones a múltiples variables, para operaciones por píxel, es un ejemplo, puede que prefiera una macro a una función ... aunque todavía depende mucho del contexto, ya que las
inline
funciones pueden ser una opción) .Es cierto que algunos de estos se basan en extensiones del compilador que no son estándar C. Lo que significa que puede terminar con un código menos portátil, o tener que hacerlo
ifdef
, por lo que solo se aprovechan cuando el compilador lo admite.Evitar la instanciación de múltiples argumentos
Teniendo en cuenta esto, ya que es una de las causas más comunes de errores en las macros (pasando,
x++
por ejemplo, cuando una macro puede incrementarse varias veces) .Es posible escribir macros que eviten efectos secundarios con múltiples instancias de argumentos.
C11 Genérico
Si desea tener una
square
macro que funcione con varios tipos y tenga soporte C11, puede hacer esto ...Expresiones de declaración
Esta es una extensión del compilador compatible con GCC, Clang, EKOPath e Intel C ++ (pero no MSVC) ;
Entonces, la desventaja de las macros es que necesita saber cómo usarlas para empezar, y que no son tan compatibles.
Un beneficio es que, en este caso, puede usar la misma
square
función para muchos tipos diferentes.fuente
No se repite la verificación de tipo de los parámetros y el código, lo que puede provocar que el código se sobrecargue. La sintaxis de macros también puede llevar a cualquier número de casos extremos extraños en los que el punto y coma o el orden de precedencia pueden interferir. Aquí hay un enlace que demuestra algo de maldad macro.
fuente
Un inconveniente de las macros es que los depuradores leen el código fuente, que no tiene macros expandidas, por lo que ejecutar un depurador en una macro no es necesariamente útil. No hace falta decir que no puede establecer un punto de interrupción dentro de una macro como puede hacerlo con las funciones.
fuente
Las funciones comprueban el tipo. Esto le brinda una capa adicional de seguridad.
fuente
Añadiendo a esta respuesta ...
Las macros son sustituidas directamente en el programa por el preprocesador (ya que básicamente son directivas del preprocesador). Entonces, inevitablemente, usan más espacio de memoria que una función respectiva. Por otro lado, una función requiere más tiempo para ser llamada y devolver resultados, y esta sobrecarga se puede evitar usando macros.
Además, las macros tienen algunas herramientas especiales que pueden ayudar con la portabilidad del programa en diferentes plataformas.
No es necesario asignar a las macros un tipo de datos para sus argumentos en contraste con las funciones.
En general, son una herramienta útil en programación. Y tanto las macroinstrucciones como las funciones se pueden utilizar según las circunstancias.
fuente
No noté, en las respuestas anteriores, una ventaja de las funciones sobre las macros que creo que es muy importante:
Las funciones se pueden pasar como argumentos, las macros no.
Ejemplo concreto: desea escribir una versión alternativa de la función estándar 'strpbrk' que aceptará, en lugar de una lista explícita de caracteres para buscar dentro de otra cadena, una función (puntero a a) que devolverá 0 hasta que un carácter sea encontró que pasa alguna prueba (definida por el usuario). Una razón por la que podría querer hacer esto es para poder explotar otras funciones de biblioteca estándar: en lugar de proporcionar una cadena explícita llena de puntuación, podría pasar 'ispunct' de ctype.h, etc. Si 'ispunct' se implementó solo como una macro, esto no funcionaría.
Hay muchos otros ejemplos. Por ejemplo, si su comparación se realiza mediante una macro en lugar de una función, no puede pasarla a 'qsort' de stdlib.h.
Una situación análoga en Python es 'imprimir' en la versión 2 frente a la versión 3 (declaración no aceptable frente a función aceptable).
fuente
Si pasa la función como un argumento a la macro, se evaluará cada vez. Por ejemplo, si llama a una de las macros más populares:
como eso
functionThatTakeLongTime se evaluará 5 veces, lo que puede reducir significativamente el rendimiento
fuente