TEMA 10

EL PREPROSESADOR DE C

Directivas del preprocesador

Como se comento en el primer capítulo el preprocesamiento es el primer paso en la étapa de compilación de un programa -esta propiedad es única del compilador de C.

El preprocesador tiene más o menos su propio lenguaje el cual puede ser una herrramienta muy poderosa para el programador. Todas las directivas de preprocesador o comandos inician con un #.

Las ventajas que tiene usar el preprocesador son:

- los programas son más fáciles de desarrollar,
- son más fáciles de leer,
- son más fáciles de modificar
- y el código de C es más transportable entre diferentes arquitecturas de máquinas.

#DEFINE

El preprocesador también permite configurar el lenguaje. Por ejemplo, para cambiar a las sentencias de bloque de código { ... } delimitadores que haya inventado el programador como inicio ... fin se puede hacer:

#define inicio {
#define fin }

Durante la compilación todas las ocurrencias de inicio y fin serán reemplazadas por su correspondiente { o } delimitador y las siguientes étapas de compilación de C no encontrarán ninguna diferencia.

La directiva #define se usa para definir constantes o cualquier sustitución de macro. Su formato es el siguiente:

#define

Por ejemplo:

#define FALSO 0
#define VERDADERO !FALSO

La directiva #define tiene otra poderosa característica: el nombre de macro puede tener argumentos. Cada vez que el compilador encuentra el nombre de macro, los argumentos reales encontrados en el programa reemplazan los argumentos asociados con el nombre de la macro. Por ejemplo:

#define MIN(a,b) (a < b) ? a : b

main()
{
int x=10, y=20;

printf("EL minimo es %d\n", MIN(x,y) );
}

Cuando se compila este programa, el compilador sustiuirá la expresión definida por MIN(x,y), excepto que x e y serán usados como los operandos. Así después de que el compilador hace la sustitución, la sentencia printf será ésta:

printf("El minimo es %d\n", (x < y) ? x : y);


Como se puede observar donde se coloque MIN, el texto será reemplazado por la definición apropiada. Por lo tanto, si en el código se hubiera puesto algo como:

x = MIN(q+r,s+t);


después del preprocesamiento, el código podría verse de la siguiente forma:

x = ( q+r < s+t ) ? q+r : s+t;


Otros ejemplos usando #define pueden ser:

#define Deg_a_Rad(X) (X*M_PI/180.0)
/* Convierte grados sexagesimales a radianes, M_PI es el valor de pi */
/* y esta definida en la biblioteca math.h */

#define IZQ_DESP_8 <<8

La última macro IZQ_DESP_8 es solamente válida en tanto el reemplazo del contexto es válido, por ejemplo: x = y IZQ_DESP_8.

El uso de la sustitución de macros en el lugar de las funciones reales tiene un beneficio importante: incrementa la velocidad del código porque no se penaliza con una llamada de función. Sin embargo, se paga este incremento de velocidad con un incremento en el tamaño del programa porque se duplica el código.

#INCLUDE

La directiva del preprocesador #include instruye al compilador para incluir otro archivo fuente que esta dado con esta directiva y de esta forma compilar otro archivo fuente. El archivo fuente que se leerá se debe encerrar entre comillas dobles o paréntesis de ángulo. Por ejemplo:

#include

#include "archivo"

Cuando se indica se le dice al compilador que busque donde estan los archivos incluidos o ``include'' del sistema. Usualmente los sistemas con UNIX guardan los archivos en el directorio /usr/include.

Si se usa la forma "archivo" es buscado en el directorio actual, es decir, donde el programa esta siendo ejecutado.

Los archivos incluidos usualmente contienen los prototipos de las funciones y las declaraciones de los archivos cabecera (header files) y no tienen código de C (algoritmos).

#UNDEF

Se usa #undef para quitar una definición de nombre de macro que se haya definido previamente. El formato general es:

#undef

El uso principal de #undef es permitir localizar los nombres de macros sólo en las secciones de código que los necesiten.

Control del preprocesador del compilador

Se puede usar el compilador gcc para controlar los valores dados o definidos en la línea de comandos. Esto permite alguna flexibilidad para configurar valores ademas de tener algunas otras funciones útiles. Para lo anterior, se usa la opción -Dmacro[=defn], por ejemplo:

gcc -DLONGLINEA=80 prog.c -o prog

que hubiese tenido el mismo resultado que

#define LONGLINEA 80

en caso de que hubiera dentro del programa (prog.c) algún #define o #undef pasaría por encima de la entrada de la línea de comandos, y el compilador podría mandar un ``warning'' sobre esta situación.

También se puede poner un símbolo sin valor, por ejemplo:

gcc -DDEBUG prog.c -o prog

En donde el valor que se toma es de 1 para esta macro.

Las aplicaciones pueden ser diversas, como por ejemplo cuando se quiere como una bandera para depuración se puede hacer algo como lo siguiente:

#ifdef DEBUG
printf("Depurando: Versión del programa 1.0\n");
#else
printf("Version del programa 1.0 (Estable)\n");
#endif

Como los comandos del preprocesador pueden estar en cualquier parte de un programa, se pueden filtrar variables para mostrar su valor, cuando se esta depurando, ver el siguiente ejemplo:
x = y * 3;

#ifdef DEBUG
printf("Depurando: variables x e y iguales a \n",x,y);
#endif

La opción -E hace que la compilación se detenga después de la étapa de preprocesamiento, por lo anterior no se esta propiamente compilando el programa. La salida del preprocesamiento es enviada a la entrada estándar. GCC ignora los archivos de entrada que no requieran preprocesamiento.

Otras directivas del preprocesador

La directiva #error forza al compilador a parar la compilación cuando la encuentra. Se usa principalmente para depuración. Por ejemplo:

#ifdef OS_MSDOS
#include
#elifdef OS_UNIX
#include "default.h"
#else
#error Sistema Operativo incorrecto
#endif

La directiva #line número ``cadena'' informa al preprocesador cual es el número siguiente de la línea de entrada. Cadena es opcional y nombra la siguiente línea de entrada. Esto es usado frecuentemente cuando son traducidos otros lenguajes a C. Por ejemplo, los mensajes de error producidos por el compilador de C pueden referenciar el nombre del archivo y la línea de la fuente original en vez de los archivos intermedios de C.

Por ejemplo, lo siguiente especifica que el contador de línea empezará con 100.

#line 100 "test.c" /* inicializa el contador de linea y nombre de archivo */
main() /* linea 100 */
{
printf("%d\n",__LINE__); /* macro predefinida, linea 102 */
printf("%s\n",__FILE__); /* macro predefinida para el nombre */
}

REGRESAR ARRIBA


REGRESAR A LA PAGINA INICIAL