Preprocesador C/C++ ( ing. preprocesador , preprocesador): un programa que prepara el código del programa en C / C++ para la compilación .
El preprocesador hace lo siguiente:
La compilación condicional le permite elegir qué código compilar en función de:
Pasos del preprocesador:
El lenguaje del preprocesador C/C++ no está completo en Turing, aunque solo sea porque es imposible hacer que el preprocesador se cuelgue usando directivas. Ver función recursiva (teoría de la computabilidad) .
Una directiva de preprocesador (línea de comando) es una línea en el código fuente que tiene el siguiente formato: #ключевое_слово параметры:
Lista de palabras clave:
Cuando se encuentran directivas #include "..."y #include <...>, donde "..." es un nombre de archivo, el preprocesador lee el contenido del archivo especificado, ejecuta directivas y sustituciones (sustituciones), reemplaza la directiva #includecon una directiva #liney el contenido del archivo procesado.
Para #include "..."buscar un archivo, se realiza en la carpeta actual y las carpetas especificadas en la línea de comando del compilador. Para #include <...>buscar un archivo, se realiza en carpetas que contienen archivos de biblioteca estándar (las rutas a estas carpetas dependen de la implementación del compilador).
Si se encuentra una directiva que #include последовательность-лексем no coincide con ninguna de las formas anteriores, considera la secuencia de tokens como texto, que, como resultado de todas las sustituciones de macros, debe dar #include <...>o #include "...". La directiva generada de esta manera se interpretará posteriormente de acuerdo con el formulario recibido.
Los archivos incluidos normalmente contienen:
La directiva #includegeneralmente se especifica al principio del archivo (en el encabezado), por lo que los archivos incluidos se denominan archivos de encabezado .
Un ejemplo de inclusión de archivos de la biblioteca estándar de C.
#include <math.h> // incluye declaraciones de funciones matemáticas #include <stdio.h> // incluye declaraciones de funciones de E/SEl uso de un preprocesador se considera ineficiente por las siguientes razones:
A partir de la década de 1970 comenzaron a aparecer métodos que reemplazaban la inclusión de archivos. Los lenguajes Java y Common Lisp usan paquetes (keyword package) (ver paquete en Java ), Pascal usa el inglés. units (palabras clave unity uses), en módulos Modula , OCaml , Haskell y Python . Diseñado para reemplazar los lenguajes C y C++ , D usa las palabras clave y . moduleimport
Las constantes del preprocesador y las macros se utilizan para definir pequeñas piezas de código.
// constante #define BUFFER_SIZE ( 1024 ) // macro #define NUMBER_OF_ARRAY_ITEMS(arreglo) (tamaño(arreglo) / tamaño(*(arreglo)))Cada constante y cada macro se sustituye por su correspondiente definición. Las macros tienen parámetros similares a funciones y se utilizan para reducir la sobrecarga de las llamadas a funciones en los casos en que la pequeña cantidad de código que llama la función es suficiente para causar un impacto notable en el rendimiento.
Ejemplo. Definición de la macro max , que toma dos argumentos: a y b .
#define max( a, b ) ( (a) > (b) ? (a) : (b) )Una macro se llama como cualquier función.
z = máx ( x , y );Después de reemplazar la macro, el código se verá así:
z = ( ( x ) > ( y ) ? ( x ) : ( y ) );Sin embargo, junto con las ventajas de usar macros en lenguaje C, por ejemplo, para definir tipos de datos genéricos o herramientas de depuración, también reducen un poco la eficiencia de su uso e incluso pueden dar lugar a errores.
Por ejemplo, si f y g son dos funciones, la llamada
z = máx ( f (), g () );no evaluará f() una vez y g() una vez , y pondrá el valor más grande en z , como es de esperar. En su lugar, una de las funciones se evaluará dos veces. Si una función tiene efectos secundarios, es probable que su comportamiento sea diferente al esperado.
Las macros de C pueden ser como funciones, creando una nueva sintaxis hasta cierto punto, y también se pueden aumentar con texto arbitrario (aunque el compilador de C requiere que el texto esté en código C sin errores o formateado como un comentario), pero tienen algunas limitaciones. como estructuras de software. Las macros similares a funciones, por ejemplo, se pueden llamar como funciones "reales", pero una macro no se puede pasar a otra función usando un puntero, porque la macro en sí no tiene dirección.
Algunos lenguajes modernos no suelen utilizar este tipo de metaprogramación utilizando macros como terminaciones de cadenas de caracteres, confiando en el cableado automático o manual de funciones y métodos, sino en otras formas de abstracción como plantillas , funciones genéricas o polimorfismo paramétrico . En particular, las funciones en línea una de las principales deficiencias de las macros en las versiones modernas de C y C++, ya que una función en línea brinda la ventaja de las macros al reducir la sobrecarga de una llamada de función, pero su dirección se puede pasar en un puntero para indirecta. llama o se utiliza como parámetro. Asimismo, el problema de las evaluaciones múltiples mencionado anteriormente en la macro max es irrelevante para las funciones integradas.
Puede reemplazar #define constantes con enumeraciones y macros con funciones inline.
Operadores # y ##Estos operadores se utilizan al crear macros. El operador # antes de un parámetro de macro lo encierra entre comillas dobles, por ejemplo:
#define make_str( bar ) # bar printf ( make_str ( 42 ) );preprocesador se convierte en:
imprimirf ( "42" );El operador ## en las macros concatena dos tokens, por ejemplo:
#define MarcarPosición( x ) x##X, x##Y, x##Ancho, x##Altura int MarcarPosición ( Objeto );preprocesador se convierte en:
int ObjectX , ObjectY , ObjectWidth , ObjectHeight ; Descripción formal de macro sustituciones1) La línea de control del siguiente formulario obliga al preprocesador a reemplazar el identificador con una secuencia de tokens en el resto del texto del programa:
#define identificador token_sequenceEn este caso, se descartan los espacios en blanco al principio y al final de la secuencia de fichas. Una línea #define repetida con el mismo identificador se considera un error si las secuencias de tokens no son idénticas (las discrepancias en los espacios en blanco no importan).
2) Una cadena de la siguiente forma, donde no debe haber espacios en blanco entre el primer identificador y el paréntesis de apertura, es una definición de macro con parámetros especificados por la lista de identificadores.
#define identificador(lista_de_identificadores) secuencia_de_tokensComo en la primera forma, los caracteres de espacio en blanco al principio y al final de la secuencia del token se descartan, y la macro solo se puede redefinir con la misma lista de parámetros de número y nombre y la misma secuencia del token.
Una línea de control como esta le dice al preprocesador que "olvide" la definición dada al identificador:
identificador #undefAplicar la directiva #undef a un identificador previamente no definido no se considera un error.
{
El proceso de sustitución se ve afectado por dos signos de operadores especiales.
}
Un signo de exclamación (!) marca las reglas responsables de la invocación recursiva y las definiciones.
Ejemplo de expansión de macros #definir gato( x, y ) x ## yLa llamada de macro "cat(var, 123)" será reemplazada por "var123". Sin embargo, llamar a "cat(cat(1, 2), 3)" no producirá el resultado deseado. Considere los pasos del preprocesador:
0: gato (gato (1, 2), 3) 1: gato (1, 2) ## 3 2: gato (1, 2) 3La operación "##" impidió la expansión adecuada de los argumentos de la segunda llamada "gato". El resultado es la siguiente cadena de tokens:
gato ( 1 , 2 ) 3donde ")3" es el resultado de concatenar el último token del primer argumento con el primer token del segundo argumento, no es un token válido.
Puede especificar el segundo nivel de macro de la siguiente manera:
#define xcat( x, y ) cat( x, y )La llamada "xcat(xcat(1, 2), 3)" será reemplazada por "123". Considere los pasos del preprocesador:
0: xgato( xgato( 1, 2 ), 3 ) 1: gato( xgato( 1, 2 ), 3 ) 2: gato( gato( 1, 2 ), 3 ) 3: gato (1 ## 2, 3) 4: gato (12, 3) 5:12##3 6:123Todo salió bien, porque el operador "##" no participó en la expansión de la macro "xcat".
Muchos analizadores estáticos no pueden procesar las macros correctamente, por lo que se reduce la calidad del análisis estático. .
Constantes generadas automáticamente por el preprocesador:
El preprocesador C proporciona la capacidad de compilar con condiciones. Esto permite la posibilidad de diferentes versiones de un mismo código. Por lo general, este enfoque se usa para personalizar el programa para la plataforma del compilador, el estado (el código depurado se puede resaltar en el código resultante) o la capacidad de verificar la conexión del archivo exactamente una vez.
En general, el programador necesita usar una construcción como:
# ifndef FOO_H # define FOO_H ...( código de archivo de encabezado )... # terminara siEsta "protección de macros" evita que un archivo de encabezado se incluya dos veces al verificar la existencia de esa macro, que tiene el mismo nombre que el archivo de encabezado. La definición de la macro FOO_H se produce cuando el preprocesador procesa por primera vez el archivo de encabezado. Luego, si se vuelve a incluir este archivo de encabezado, FOO_H ya está definido, lo que hace que el preprocesador omita todo el texto de este archivo de encabezado.
Se puede hacer lo mismo incluyendo la siguiente directiva en el archivo de encabezado:
# pragma una vezLas condiciones del preprocesador se pueden especificar de varias maneras, por ejemplo:
#ifdef x ... #más ... # terminara sio
#ifx ... #más ... # terminara siEste método se usa a menudo en los archivos de encabezado del sistema para probar varias capacidades, cuya definición puede variar según la plataforma; por ejemplo, la biblioteca Glibc utiliza macros de verificación de características para verificar que el sistema operativo y el hardware las admitan (las macros) correctamente mientras mantienen la misma interfaz de programación.
La mayoría de los lenguajes de programación modernos no aprovechan estas características, confiando más en las sentencias condicionales tradicionales if...then...else..., dejando al compilador con la tarea de extraer código inútil del programa que se está compilando.
Ver dígrafos y trigrafos en lenguajes C/C++.
El preprocesador procesa los dígrafos “ %:” (“ #”), “ %:%:” (“ ##”) y los trígrafos “ ??=” (“ #”), “ ??/” (“ \”).
El preprocesador considera que la secuencia " %:%: " son dos tokens al procesar el código C y un token al procesar el código C++.
lenguaje de programación c | |
---|---|
compiladores |
|
bibliotecas | |
Peculiaridades | |
algunos descendientes | |
C y otros lenguajes |
|
Categoría:Lenguaje de programación C |