Curso de C++ (Página 25)

pagina024 Principal pagina026

CAPITULO 25 El preprocesador

El preprocesador analiza el fichero fuente antes de la fase de compilación real, y realiza las sustituciones de macros y procesa las directivas del preprocesador.También se eliminan los comentarios.

Una directiva de preprocesador es una línea cuyo primer carácter es un #.

A continuación se describen las directivas del preprocesador, aunque algunas ya las hemos visto antes.

Directiva #define:

La directiva "#define", sirve para definir macros. Esto suministra un sistema para la sustitución de palabras, con y sin parámetros.

Sintaxis:

#define identificador_de_macro <secuencia>

El preprocesador sustituirá cada ocurrencia del identificador_de_macro en el fichero fuente, por la secuencia con algunas excepciones. Cada sustitución se conoce como una expansión de la macro. La secuencia es llamada a menudo cuerpo de la macro.

Si la secuencia no existe, el identificador_de_macro será eliminado cada vez que aparezca en el fichero fuente.

Después de cada expansión individual, se vuelve a examinar el texto expandido a la búsqueda de nuevas macros, que serán expandidas a su vez. Esto permite la posibilidad de hacer macros anidadas. Si la nueva expansión tiene la forma de una directiva de preprocesador, no será reconocida como tal.

Existen otras restricciones a la expansión de macros:

Las ocurrencias de macros dentro de literales, cadenas, constantes alfanuméricas o comentarios no serán expandidas.

Una macro no será expandida durante su propia expansión, así #define A A, no será expandida indefinidamente.

No es necesario añadir un punto y coma para terminar una directiva de preprocesador. Cualquier carácter que se encuentre en una secuencia de macro, incluido el punto y coma, aparecerá en la expansión de la macro. La secuencia termina en el primer retorno de línea encontrado. Las secuencias de espacios o comentarios en la secuencia, se expandirán como un único espacio.

Directiva #undef:

Sirve para eliminar definiciones de macros previamente definidas. La definición de la macro se olvida y el identificador que indefinido.

Sintaxis:

#undef identificador_de_macro

La definición es una propiedad importante de un identificador. Las directivas condicionales #ifdef e #ifndef se basan precisamente en esta propiedad de los identificadores. Esto ofrece un mecanismo muy potente para controlar muchos aspectos de la compilación.

Después de que una macro quede indefinida puede ser definida de nuevo con #define, usando la misma u otra definición.

Si se intenta definir un identificador de macro que ya esté definido, se producirá un aviso, un warning, si la definición no es exactamente la misma. Es preferible usar un mecanismo como este para detectar macros existentes:

#ifndef NULL 
  #define NULL 0L 
#endif 

De éste modo, la línea del #define se ignorará si el símbolo NULL ya está definido.

Directivas #if, #elif, #else y #endif

Permiten hacer una compilación condicional de un conjunto de líneas de código.

Sintaxis:

#if expresión-constante-1 
<sección-1> 
#elif <expresión-constante-2> 
<sección-2> 
.
.
.
#elif <expresión-constante-n> 
<sección-n> 
<#else> 
<sección-final> 
#endif 

Todas las directivas condicionales deben completarse dentro del mismo fichero. Sólo se compilarán las líneas que estén dentro de las secciones que cumplan la condición de la expresión constante correspondiente.

Estas directivas funcionan de modo similar a los operadores condicionales C. Si el resultado de evaluar la expresión constante 1, que puede ser una macro, es distinto de cero (true), las líneas representadas por sección-1, ya sean líneas de comandos, macros o incluso nada, serán compiladas. En caso contrario, si el resultado de la evaluación de la expresión constante 1, es cero (false), la sección-1 será ignorada, no se expandirán macros ni se compilará.

En el caso de ser distinto de cero, después de que la sección-1 sea preprocesada, el control pasa al #endif correspondiente, con lo que termina la secuencia condicional. En el caso de ser cero, el control pasa al siguiente línea #elif, si existe, donde se evaluará la expresión constante 2. Si el resultado es distinto de cero, se procesrá la sección-2, y después el control pasa al correspondiente #endif. Por el contrario, si el resultaado de la expresión constante 2 es cero, el control pasa al siguiente #elif, y así sucesivamente, hasta que se encuentre un #else o un #endif. El #else, que es opcional, se usa como una condición alternativa para el caso en que todas la condiciones anteriores resulten falsas. El #endif termina la secuencia condicional.

Cada sección procesada puede contener a su vez directivas condicionales, anidadas hasta cualquier nivel, cada #if debe corresponderse con el #endif más cercano.

El objetivo de una red de este tipo es que sólo una sección, inclusive vacía, sea compilada. Las secciones ignoradas sólo son relevantes para evaluar las condiciones anidadas, es decir asociar cada #if con su #endif.

Las expresiones constantes deben poder ser evaluadas como valores enteros.

Directivas #ifdef e #ifndef:

Estas directivas permiten comprobar si un identificador está o no actualmente definido, es decir, si un #define ha sido previamente procesado para el identificador y si sigue definido.

Sintaxis:

#ifdef identifier

#ifndef identifier

La línea:

#ifdef identificador

tiene exactamente el mismo efecto que

#if 1

si el identificador está actualmente definido, y el mismo efecto que

#if 0 

si el identificador no está definido.

#ifndef compueba la no definición de un identificador, así la línea

#ifndef identificador

tiene el mismo efecto que

#if 0

si el identificador está definido, y el mismo efecto que

#if 1

si el identificador no está definido.

Por lo demás, la sintaxis es la misma que para #if, #elif, #else, y #endif.

Un identificador definido como nulo, se considera definido.

Directiva #error:

Esta directiva se suele incluir en sentencias condicionales de preprocesador para detectar condiciones no deseadas durante la compilación. En un funcionamiento normal estas condiciones serán falsas, pero cuando la condición es verdadera, es preferible que el compilador muestre un mensaje de error y detenga la fase de compilación. Para hacer esto se debe introducir esta directiva en una sentencia condicional que detecte el caso no deseado.

Sintaxis:

#error mensaje_de_error

Esta directiva genera el mensaje:

Error: nombre_de_fichero nº_línea : Error directive: mensaje_de_error

Directiva #include:

La directiva "#include", como ya hemos visto, sirve para insertar ficheros externos dentro de nuestro fichero de código fuente. Estos ficheros son conocidos como ficheros incluidos, ficheros de cabecera o "headers".

Sintaxis:

#include <nombre de fichero cabecera>

#include "nombre de fichero de cabecera"

#include identificador_de_macro

El preprocesador elimina la línea "#include" y, conceptualmente, la sustituye por el fichero especificado. El tercer caso haya el nombre del fichero como resultado de aplicar la macro.

El código fuente en si no cambia, pero el compilador "ve" el fichero incluido. El emplazamiento del #include puede influir sobre el ámbito y la duración de cualquiera de los identificadores en el interior del fichero incluido.

La diferencia entre escribir el nombre del fichero entre "<>" o """", está en el algoritmo usado para encontrar los ficheros a incluir. En el primer caso el preprocesador buscará en los directorios "include" definidos en el compilador. En el segundo, se buscará primero en el directorio actual, es decir, en el que se encuentre el fichero fuente, si no existe en ese directorio, se trabajará como el primer caso.

Si se proporciona el camino como parte del nombre de fichero, sólo se buscará es el directorio especificado.

Directiva #line:

No se usa, se trata de una característica heredada de los primitivos compiladores C.

Sintaxis:

#line constante_entera <"nombre_de_fichero">

Esta directiva se usa para sustituir los números de línea en los programas de referencias cruzadas y en mensajes de error. Si el programa consiste en secciones de otros ficheros fuente unidas en un sólo fichero, se usa para sustituir las referencias a esas secciones con los números de línea del fichero original, como si no se hubiera integrado en un único fichero.

La directiva #line indica que la siguiente línea de código proviene de la línea "constante_entera" del fichero "nombre_de_fichero". Una vez que el nombre de fichero ha sido registrado, sucesivas apariciones de la directiva #line relativas al mismo fichero pueden omitir el argumento del nombre.

Las macros serán expandidas en los argumentos de #line del mismo modo que en la directiva #include.

La directiva #line se usó originalmente para utilidades que producían como salida código C, y no para código escrito por personas.

Directiva #pragma:

Sintaxis:

#pragma nombre-de-directiva

Con esta directiva, cada compilador puede definir sus propias directivas, que no interferiran con las de otros compiladores. Si el compilador no reconoce el nombre-de-directiva, ignorará la línea completa sin producir ningún tipo de error o warning.


pagina024 Principal pagina026