Las macros forman parte tanto de C como de FORTRAN (aunque la noción equivalente a una macro en FORTRAN, la
función sentencia , ha sido vilipendiada por la comunidad FORTRAN y no sobrevivirá mucho tiempo más).
La función sentencia se ha eliminado de FORTRAN 90. En los programas en lenguaje C, las macros se crean mediante una directiva
#define
, como se demuestra aquí:
#define promedio(x,y) ((x+y)/2)
main (){
float q = 100, p = 50;float a;
a = promedio(p,q);printf ("%f\n",a);
}
El primer paso de compilación de un programa en C consiste en pasar por el preprocesador de C,
cpp . Ello ocurre automáticamente cuando usted invoca al compilador.
cpp expande las sentencias
#define
en línea, reemplazando el patrón coincidente por la definición de la macro. En el programa de arriba, la sentencia:
a = promedio(p,q);
es reemplazada por:
a = ((p+q)/2);
Debe usted ser cuidadoso al definir la macro, porque literalmente reemplaza al patrón que cpp encuentra. Por ejemplo, si la definición de la macro decía:
#define multiplicar(a,b) (a*b)
y usted lo invocó como:
c = multiplicar(x+t,y+v);
la expansión resultante será
x+t*y+v
— que probablemente no es lo que usted pretendía.
Si es usted un programador en C, puede que esté usando macros sin siquiera percatarse. Muchos archivos de cabecera en C ( .h ) contienen definiciones de macros. De hecho, algunas funciones "estándar" de biblioteca en C en realidad son macros contenidas en tales archivos. Por ejemplo, la función getchar puede enlazarse cuando construye su programa. Si tiene una sentencia:
#include<stdio.h>
en su archivo, getchar se reemplazará con una definición de macro a tiempo de compilación, reemplazando la función de biblioteca de C.
También puede usted hacer que las macros de cpp trabajen para los programas en FORTRAN. Algunos programadores prefieren usar para FORTRAN el preprocesador estándar de UNIX, m4 . Por ejemplo, una versión FORTRAN del programa en C anterior pudiera verse así:
#define PROMEDIO(X,Y) ((X+Y)/2)
CPROGRAM MAIN
REAL A,P,QDATA P,Q /50.,100./
A = PROMEDIO(P,Q)WRITE (*,*) A
END
Sin algo de preparación, la sentencia
#define
será rechazada por el compilador de FORTRAN. El programa debe preprocesarse previamente mediante
cpp para reemplazar el uso de
PROMEDIO
con su definición de macro. Ello convierte a la compilación en un procedimiento de dos pasos, pero que no debe ser una carga mayor, especialmente si está construyendo sus programas bajo el control del programa de utilidad
make . También le sugerimos almacenar los programas en FORTRAN que contienen directivas para
cpp mediante la nomenclatura
filename.F para distinguirlos de los programas FORTRAN sin adornos. Sólo asegúrese de hacer los cambios sólo a los archivos
.F , y no a la salida de
cpp . Así es como debe preprocesar los archivos
.F de FORTRAN a mano:
% /lib/cpp -P<promedio.F>promedio.f
% f77 promedio.f -c
El compilador de FORTRAN nunca ve el código original. En vez de ello, la definición de la macro se sustituye en línea, tal y como si la hubiera tecleado usted mismo:
C
PROGRAM MAINREAL A,P,Q
DATA P,Q /50.,100./ A = ((P+Q)/2)WRITE (*,*) A
END
Por cierto, algunos compiladores de FORTRAN también reconocen la extensión .F , haciendo innecesario el proceso en dos pasos. Si el compilador ve la extensión .F invoca automáticamente a cpp , y desecha el archivo .f intermedio. Trate de compilar un archivo .F en su computadora para ver si funciona.
También, tome conciencia de que la expansión de macros puede provocar que las líneas de código fuente excedan la columna 72, lo cuál hará que su compilador de FORTRAN reclame (o peor, que le pase desapercibido). Algunos compiladores soportan líneas de entrada más largas de 72 caracteres. En los compiladores de Sun la opción –e permite extender las líneas de entrada hasta 132 caracteres de longitud.
Procedimientos en línea
Las definiciones de macros tienden a ser muy cortas, usualmente de sólo una sentencia de longitud. A veces tiene usted porciones de código ligeramente más largas (pero no demasiado largas), que también pueden obtener beneficio si se copian en línea, en vez de invocarse como subrutina o función. Nuevamente, la razón de hacerlo es eliminar la sobrecarga debida a la invocación de procedimientos, y exponer el paralelismo. Si su compilador es capaz de definir subrutinas y funciones inline (en línea) al interior de los módulos que las invocan, entonces dispone de una forma muy natural y portátil de escribir código modular sin sufrir del costo debido a la invocación de la subrutina.
Dependiendo del vendedor, puede preguntarle al compilador si soporta la declaración de procedimientos en línea mediante:
- Especificar desde la línea de comandos aquellas rutinas que deben insertarse en línea
- Poner directivas para la declaración en línea dentro del código fuente
- Permitir que el compilador decida automáticamente qué poner en línea
Las directivas y las opciones de la línea de comandos del compilador no están estandarizadas, así que deberá revisar la documentación de su compilador. Desafortunadamente, puede que nuevamente descubra que no hay tal característica (“nuevamente,” siempre nuevamente), o que es una característica extra que por la que deberá pagar. La tercera forma de declaración en línea de la lista, la automática, sólo la tienen disponible unos pocos vendedores. La declaración en línea automática depende de que el compilador sea sofisticado y pueda ver las definiciones de varios módulos a la vez.
Unas últimas palabras de precaución respecto a la colocación de procedimientos en línea. Es fácil exagerar en su uso. Si todo termina enquistado en el cuerpo o en sus padres, el ejecutable resultante puede ser tan grande que repetidamente desborde la cache de instrucciones y se convierta en una pérdida de rendimiento neto. Nuestro consejo es que use la información de invocador/invocado que le proporcionan los perfiladores para tomar decisiones inteligentes acerca de la ubicación en línea, en vez de tratar de aplicarlo a cada subrutina que genere. De nuevo, los mejores candidatos generalmente son aquellas rutinas pequeñas que se invocan muy a menudo.