C - Preprocesadores

los C Preprocessorno es parte del compilador, pero es un paso separado en el proceso de compilación. En términos simples, un preprocesador de C es solo una herramienta de sustitución de texto e indica al compilador que realice el preprocesamiento requerido antes de la compilación real. Nos referiremos al preprocesador de C como CPP.

Todos los comandos del preprocesador comienzan con un símbolo de almohadilla (#). Debe ser el primer carácter que no esté en blanco y, para facilitar la lectura, una directiva de preprocesador debe comenzar en la primera columna. La siguiente sección enumera todas las directivas importantes del preprocesador:

No Señor. Directiva y descripción
1

#define

Sustituye a una macro de preprocesador.

2

#include

Inserta un encabezado particular de otro archivo.

3

#undef

Anula la definición de una macro de preprocesador.

4

#ifdef

Devuelve verdadero si esta macro está definida.

5

#ifndef

Devuelve verdadero si esta macro no está definida.

6

#if

Comprueba si una condición de tiempo de compilación es verdadera.

7

#else

La alternativa para #if.

8

#elif

#else y #if en una sola declaración.

9

#endif

Finaliza el preprocesador condicional.

10

#error

Imprime mensaje de error en stderr.

11

#pragma

Emite comandos especiales para el compilador, utilizando un método estandarizado.

Ejemplos de preprocesadores

Analice los siguientes ejemplos para comprender varias directivas.

#define MAX_ARRAY_LENGTH 20

Esta directiva le dice al CPP que reemplace las instancias de MAX_ARRAY_LENGTH con 20. Use #define para las constantes para aumentar la legibilidad.

#include <stdio.h>
#include "myheader.h"

Estas directivas le dicen al CPP que obtenga stdio.h de System Librariesy agregue el texto al archivo fuente actual. La siguiente línea le dice a CPP que obtengamyheader.h del directorio local y agregue el contenido al archivo fuente actual.

#undef  FILE_SIZE
#define FILE_SIZE 42

Le dice al CPP que anule la definición de FILE_SIZE existente y lo defina como 42.

#ifndef MESSAGE
   #define MESSAGE "You wish!"
#endif

Le dice al CPP que defina MESSAGE solo si MESSAGE aún no está definido.

#ifdef DEBUG
   /* Your debugging statements here */
#endif

Le dice al CPP que procese las declaraciones adjuntas si DEBUG está definido. Esto es útil si pasa el indicador -DDEBUG al compilador gcc en el momento de la compilación. Esto definirá DEBUG, por lo que puede activar y desactivar la depuración sobre la marcha durante la compilación.

Macros predefinidas

ANSI C define una serie de macros. Aunque cada uno está disponible para su uso en programación, las macros predefinidas no deben modificarse directamente.

No Señor. Macro y descripción
1

__DATE__

La fecha actual como un carácter literal en formato "MMM DD AAAA".

2

__TIME__

La hora actual como un carácter literal en formato "HH: MM: SS".

3

__FILE__

Contiene el nombre del archivo actual como una cadena literal.

4

__LINE__

Contiene el número de línea actual como una constante decimal.

5

__STDC__

Definido como 1 cuando el compilador cumple con el estándar ANSI.

Probemos con el siguiente ejemplo:

#include <stdio.h>

int main() {

   printf("File :%s\n", __FILE__ );
   printf("Date :%s\n", __DATE__ );
   printf("Time :%s\n", __TIME__ );
   printf("Line :%d\n", __LINE__ );
   printf("ANSI :%d\n", __STDC__ );

}

Cuando el código anterior en un archivo test.c se compila y ejecuta, produce el siguiente resultado:

File :test.c
Date :Jun 2 2012
Time :03:36:24
Line :8
ANSI :1

Operadores de preprocesadores

El preprocesador de C ofrece los siguientes operadores para ayudar a crear macros:

El operador macro de continuación (\)

Normalmente, una macro se limita a una sola línea. El operador de continuación de macro (\) se utiliza para continuar una macro que es demasiado larga para una sola línea. Por ejemplo

#define  message_for(a, b)  \
   printf(#a " and " #b ": We love you!\n")

El operador Stringize (#)

El operador de cadena o signo de número ('#'), cuando se utiliza dentro de una definición de macro, convierte un parámetro de macro en una constante de cadena. Este operador solo se puede utilizar en una macro que tenga un argumento o una lista de parámetros especificados. Por ejemplo

#include <stdio.h>

#define  message_for(a, b)  \
   printf(#a " and " #b ": We love you!\n")

int main(void) {
   message_for(Carole, Debra);
   return 0;
}

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

Carole and Debra: We love you!

El operador de pegado de tokens (##)

El operador de pegado de tokens (##) dentro de una definición de macro combina dos argumentos. Permite que dos tokens separados en la definición de macro se unan en un solo token. Por ejemplo

#include <stdio.h>

#define tokenpaster(n) printf ("token" #n " = %d", token##n)

int main(void) {
   int token34 = 40;
   tokenpaster(34);
   return 0;
}

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

token34 = 40

Sucedió así porque este ejemplo da como resultado la siguiente salida real del preprocesador:

printf ("token34 = %d", token34);

Este ejemplo muestra la concatenación del token ## n en token34 y aquí hemos usado ambos stringize y token-pasting.

El operador definido ()

El preprocesador definedEl operador se usa en expresiones constantes para determinar si un identificador se define usando #define. Si se define el identificador especificado, el valor es verdadero (distinto de cero). Si el símbolo no está definido, el valor es falso (cero). El operador definido se especifica de la siguiente manera:

#include <stdio.h>

#if !defined (MESSAGE)
   #define MESSAGE "You wish!"
#endif

int main(void) {
   printf("Here is the message: %s\n", MESSAGE);  
   return 0;
}

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

Here is the message: You wish!

Macros parametrizados

Una de las poderosas funciones del CPP es la capacidad de simular funciones utilizando macros parametrizadas. Por ejemplo, podríamos tener algún código para elevar al cuadrado un número de la siguiente manera:

int square(int x) {
   return x * x;
}

Podemos reescribir encima del código usando una macro de la siguiente manera:

#define square(x) ((x) * (x))

Las macros con argumentos deben definirse mediante la #definedirectiva antes de que se puedan utilizar. La lista de argumentos está entre paréntesis y debe seguir inmediatamente al nombre de la macro. No se permiten espacios entre el nombre de la macro y los paréntesis abiertos. Por ejemplo

#include <stdio.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void) {
   printf("Max between 20 and 10 is %d\n", MAX(10, 20));  
   return 0;
}

Cuando se compila y ejecuta el código anterior, produce el siguiente resultado:

Max between 20 and 10 is 20