Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


1.4.1  El preprocesador

§1  Sinopsis

Hemos señalado ( 1.4) que en los lenguajes compilados como C++, antes de la compilación propiamente dicha, el fichero fuente es sometido a un programa denominado preprocesador que se encarga de realizar ciertas transformaciones en el código escrito por el programador. Básicamente su tarea consiste en tres tipos de acciones: incluir ciertas cosas; modificar otras, y eliminar unas terceras. El resultado es lo que se denomina  unidad de compilación ( 1.4.2).


§2  El preprocesador es un programa procesador-traductor de texto ASCII, recursivo, inteligente y sumamente potente.  Por ejemplo, puede incluir en un punto del fuente el contenido de un fichero de texto, que en una segunda pasada sufra nuevas modificaciones.  Con frecuencia los programadores experimentados construyen sus propias macros, defines y directivas condicionales de forma que casi llegan a construir un nuevo lenguaje dentro del lenguaje.  El propio C++ que ve el programador está construido en gran parte sobre directivas de preprocesado muy sofisticadas, basta echar un vistazo a los ficheros de cabecera de la Librería Estándar ( 5) para comprobar a que niveles de refinamiento puede ser llevado, y percatarse de las profundas transformaciones que sufre el fuente antes que sea realmente entregado al compilador.

Desde el punto de vista del programador, la parte más interesante es que el comportamiento del preprocesador está gobernado por pautas explicitadas en las denominadas directivas de preproceso ( 4.9.10).  Estas directivas son sentencias que se sitúan normalmente al principio del código fuente (tienen validez desde el punto de aparición hasta el final del fichero).

Siguiendo las pautas indicadas en estas directivas, el preprocesador obtiene un código (preprocesado) que será entregado como una unidad de compilación ( 1.4.2) al analizador sintáctico y de este al compilador ( 1.4).  Las directivas de preproceso las más corrientes son los include (incluir trozos de código en el fuente tomados de otros ficheros 4.9.10g) y las macros (cambiar ciertas palabras por otras o por trozos de código más complejo 4.9.10b).  Para dar una idea de las profundas transformaciones y añadidos que realiza el preprocesador sobre un sencillo fuente, considere que el preproceso del programa del ejemplo que sigue () supone el proceso de 18.490 líneas.

Es muy raro que el preprocesador sea invocado como programa independiente. Lo normal es que sea traído automáticamente a ejecución como primer paso de la compilación por el programa supervisor ( 1.4.0).  Sin embargo hay ocasiones, durante la depuración de errores, en que interesa ver en que cosa se ha convertido nuestro código después de la fase de preproceso. Por ejemplo, cuando "inventamos" macros muy sofisticadas que en principio no funcionan como esperamos.  En estos casos el preprocesador puede generar un fichero de salida con números de línea que se refieren a las originales del código fuente.  Esta unidad de compilación puede ya ser enviada al compilador, aunque lo normal es que este fichero sea utilizado meramente con fines de comprobación.

En el  caso de C++Builder, el preprocesador es el fichero Cpp32.exe; este programa genera un fichero de salida con el mismo nombre del fuente en el mismo directorio y terminación .I, aunque puede especificarse la dirección de salida, la inclusión o no de números de líneas, etc.  Por ejemplo, el comando:

cpp32 -ID:;E:\borlandCPP\Include pa.c

procesa el fuente pa.c del ejemplo ( 1.4.0) y genera un fichero de salida pa.i de más de 954 KB frente a los 4 KB originales del texto fuente, o de 307 KB si se procesa con el comando:

cpp32 -ID:;E:\borlandCPP\Include -P -Sr -Ss pa.c

§3  Fases del preprocesado

Aunque en general se considera que el preprocesado es la primera parte de la compilación, y se piensa en él como un solo proceso, en realidad se compone de varias fases que describimos brevemente [1]:

§3.1  Tokenizado léxico

En una primera fase todos los caracteres del fuente son mapeados, produciéndose una representación interna del fuente y se traducen los trígrafos ( 3.2.3e) a sus caracteres equivalentes.

§3.2  Empalmado de líneas

El programa realiza una conversión de líneas físicas a líneas lógicas. Por ejemplo, las líneas terminadas en barra invertida \ (ASCII 92) seguida de nueva línea NL (ASCII 10) son unidas a la siguiente (a menos que esté vacía, cada línea debe terminar en un NL que no esté precedido por la barra invertida).

§3.3  Tokenización

En esta fase, los comentarios ( 3.1) son sustituidos por un espacio (ASCII 32);  los espacios redundantes son eliminados, y el código queda reducido a tokens de preprocesado separados por caracteres de separación ( 1.3.1).

§3.4  Preprocesado

A continuación se realiza el preprocesado propiamente dicho.  Se ejecutan los include ( 4.9.10g) y se sustituyen las macros por el código correspondiente ( 4.9.10b).  Es importante señalar que el preprocesador es recursivo, por lo que cualquier texto incluido con un include es sometido a los pasos anteriores (a su vez puede contener comentarios, otros include, etc.)

§3.5  Mapeo de caracteres

Para la redacción del código fuente puede haberse utilizado un editor con un juego de caracteres arbitrario, pero el compilador los reduce al juego de caracteres US-ASCII ( 2.2.1a);  también son traducidas las secuencias de escape ( 3.2.3e) a sus caracteres correspondientes. El código es reducido al juego de caracteres de ejecución.

§3.6  Concatenación de cadenas

En esta fase, las cadenas literales adyacentes son unidas en una sola ( 3.2.3f). Por ejemplo:  "Esto es"  " una "  "cadena." queda reducida a:  "Esto es una cadena.".

  Inicio.


[1]  El detalle puede variar de un compilador a otro; la descripción corresponde a MS Visual C++ 6.0, pero básicamente todos siguen la misma pauta.