2009-10-24 19 views
11

que estaba leyendo un código escrito en C esta tarde, y en la parte superior de el archivo fue el HASH macro-función como:Al utilizar macros tipo función en C

#define HASH(fp) (((unsigned long)fp)%NHASH) 

Esto me dejó pensando , ¿por qué alguien elegiría implementar una función de esta manera usando una macro similar a la función en lugar de implementar como una función normal C normal? ¿Cuáles son las ventajas y desventajas de de cada implementación?

¡Muchas gracias!

+0

Como las funciones hash van, eso es bastante débil, pero parece que los datos que tiene que tratar también son bastante simples. –

+0

En realidad, no es muy brillante hacer de esto un 'macro'. Una [función estática en línea] (http://stackoverflow.com/questions/7762731/whats-the-difference-between-static-and-static-inline-function) es probablemente más apropiada. * C * ha estado en línea desde 1999 y 'gcc' incluso antes de eso. Esta pregunta se ha citado como duplicación [kfifo use of define] (http://stackoverflow.com/questions/16117304/why-is-kfifo-h-so-full-of-define-statements). Si ** MS-Windows ** es la razón por la que las personas dicen que esta macro es razonable, ¿cómo están relacionadas? –

Respuesta

3

Por un lado, las macros son malas porque están hechas por el preprocesador, que no entiende nada sobre el lenguaje y reemplaza el texto. Por lo general, tienen muchas limitaciones. No puedo ver uno arriba, pero generalmente las macros son soluciones feas.

Por otro lado, en ocasiones son incluso más rápidos que un método static inline. Estaba optimizando mucho un programa corto y descubrí que llamar a un método static inline lleva aproximadamente el doble de tiempo (solo sobrecarga, no el cuerpo de la función real) en comparación con una macro.

+0

Eso es bueno saber. ¡Gracias! Solo curiosidad, ¿qué herramienta usabas para optimizar tu código C? No he intentado la optimización antes, y me gustaría intentarlo. – Ralph

+0

¿Herramienta? Vim, 'gprof', y el venerable comando' time'. 'gprof' es un perfilador bastante bueno con poca sobrecarga, aunque debes tener cuidado porque no sigue las macros (no las ve, otra de las desventajas de las macros). – pavpanchekha

+0

¿Qué versión de compilador usaba y qué nivel de optimización estaba solicitando? –

0

El C Preprocessor se puede utilizar para crear funciones en línea. En su ejemplo, el código parecerá llamar a la función HASH, pero en su lugar es solo código en línea.

Las ventajas de realizar funciones de macro se eliminaron cuando C++ introdujo funciones en línea. Muchas API antiguas como MFC y ATL todavía usan funciones de macro para hacer trucos de preprocesador, pero solo deja el código intrincado y más difícil de leer.

1

Su ejemplo no es realmente una función en absoluto,

 
#define HASH(fp) (((unsigned long)fp)%NHASH) 
// this is a cast ^^^^^^^^^^^^^^^ 
// this is your value 'fp'  ^^ 
// this is a MOD operation   ^^^^^^ 

yo creo, que esto era sólo una forma de escribir código más legible con la fundición y opration mod envuelto en una sola macro 'HASH(fp)'


Ahora, si usted decide escribir una función para esto, sería probablemente verá así,

 
int hashThis(int fp) 
{ 
    return ((fp)%NHASH); 
} 

Toda una exageración para una función, ya que,

  • introduce un punto de llamada
  • introduce la pila de llamadas de configuración y restaurar
+0

muy buen punto! – Ralph

24

macros así evitar la sobrecarga de una llamada de función.

Puede que no parezca mucho.Pero en su ejemplo, la macro se convierte en 1-2 instrucciones en lenguaje máquina, dependiendo de su CPU:

  • obtener el valor de FP de la memoria y la puso en un registro
  • Tomar el valor en el registro , hacer un módulo (%) cálculo de un valor fijo, y dejar que en el mismo registro

mientras que la función equivalente sería mucho más instrucciones en lenguaje máquina, en general, algo así como

  • Pegar el valor de FP en la pila
  • Llame a la función, que también pone el siguiente (regreso) dirección en la pila
  • Tal vez construir un marco de pila dentro de la función, dependiendo de la arquitectura de la CPU y de la convención ABI
  • Obtener el valor de FP de la pila y lo puso en un registro
  • tomar el valor en el registro, hacer un módulo (%) cálculo de un valor fijo, y dejar que en el mismo registro
  • Tal vez tomar la valor del registro y ponerlo de nuevo en la pila, según la CPU y ABI
  • Si pila se haya construido, desenrollarlo
  • pop la dirección de retorno de la pila y reanudar la ejecución de instrucciones no

un código mucho más, ¿eh? Si está haciendo algo así como renderizar cada una de las decenas de miles de píxeles en una ventana en una GUI, las cosas se ejecutan muchísimo más rápido si utiliza la macro.

Personalmente, prefiero usar C++ inline como más legible y menos propenso a errores, pero las líneas en línea también son realmente más una sugerencia para el compilador que no tiene que tomar. Las macros de preprocesador son un martillo con el que el compilador no puede discutir.

+0

wow, tienes razón, ¡así hay más código para implementarlo como una función! – Ralph

+5

¡Para eso están las funciones en línea! :) – LiraNuna

+0

LiraNuna: las funciones en línea no son parte de ANSI C90, que es lo que admiten la mayoría de los compiladores. Solo están en C99 o C++ –

3

La razón más común (y la mayoría de las veces incorrecta) que las personas dan para usar macros (en "antigua C simple") es el argumento de la eficiencia. Usarlos para la eficiencia está bien si realmente ha perfilado su código y está optimizando un verdadero cuello de botella (o está escribiendo una función de biblioteca que podría ser un cuello de botella para alguien algún día). Pero la mayoría de las personas que insisten en usarlos en realidad no han analizado nada y solo están creando confusión donde no agrega ningún beneficio.

Las macros también se pueden utilizar para algunas sustituciones de tipo búsqueda y reemplazo útiles que el lenguaje C normal no es capaz de hacer.

Algunos de los problemas que he tenido para mantener el código escrito por usuarios de macro es que las macros pueden parecerse a funciones pero no aparecen en la tabla de símbolos, por lo que puede ser muy molesto intentar rastrearlas hasta sus orígenes en extenso de los conjuntos de códigos (¿dónde está esto definido ?!). Escribir macros en ALL CAPS es obviamente útil para los lectores futuros.

Si son sustituciones más que simples, también pueden crear cierta confusión si tiene que rastrearlas mediante un depurador.

+0

¿podría dar un ejemplo de una de estas sustituciones de búsqueda y reemplazo que solo se puede hacer con las macros que usted menciona? – Ralph

+3

Líneas generadoras de código como: #define COLORVAL (color, val) int color ## _ número = val; Yo no haría esto yo mismo demasiado, pero si necesitas líneas para rojo, azul, verde, ..., negro, entonces puede ser útil. – JXG

+0

p.212 del "Manual del enemigo de Unix" (p.248 en el .pdf http://www.simson.net/ref/ugh.pdf, de http://www.cs.washington.edu/homes/ weise/uhh-download.html) discute algunas dificultades relacionadas. – Kilo

16

Una ventaja importante de la implementación basada en macro es que no está vinculada a ningún tipo concreto de parámetro. Una macro de función en C actúa, en muchos aspectos, como una función de plantilla en C++ (las plantillas en C++ nacieron como macros "más civilizadas", por cierto). En este caso particular, el argumento de la macro no tiene un tipo concreto.Podría ser absolutamente cualquier cosa que sea convertible al tipo unsigned long. Por ejemplo, si el usuario lo desea (y si están dispuestos a aceptar las consecuencias definidas por la implementación), pueden pasar tipos de puntero a esta macro.

De todos modos, tengo que admitir que esta macro no es el mejor ejemplo de la flexibilidad de macros independiente del tipo, pero en general, la flexibilidad es práctica con bastante frecuencia. De nuevo, cuando cierta función es implementada por una función, está restringida a tipos de parámetros específicos. En muchos casos, para aplicar una operación similar a diferentes tipos, es necesario proporcionar varias funciones con diferentes tipos de parámetros (y diferentes nombres, ya que esto es C), mientras que lo mismo se puede hacer con una macro de función. Por ejemplo, macro

#define ABS(x) ((x) >= 0 ? (x) : -(x)) 

trabaja con todo tipo de aritmética, mientras que la implementación basada en la función tiene que proporcionar un buen número de ellos (estoy implicando el estándar abs, labs, llabs y fabs). (Y sí, soy consciente de los peligros tradicionalmente mencionados de tales macro.)

Las macros no son perfectas, pero la máxima popular sobre "macros funcionales ya no son necesarias debido a las funciones en línea" es simplemente una tontería . Para reemplazar completamente las macros de función, C va a necesitar plantillas de funciones (como en C++) o al menos sobrecarga de funciones (como en C++ de nuevo). Sin esa función, las macros son y seguirán siendo la herramienta principal extremadamente útil en C.

Cuestiones relacionadas