c++ - que - incluir un archivo c en otro
¿El archivo de encabezado incluido solo una vez en todo el programa? (4)
Sé que esta es una pregunta común, pero todavía no puedo entender completamente.
En un programa de C o C ++ generado a partir de múltiples archivos diferentes de origen y encabezado, ¿se incluirá cada archivo de encabezado solo una vez en todo el código cuando se usen los protectores de encabezado?
Alguien me dijo anteriormente que un archivo de encabezado (con guardas de inclusión) se incluirá solo una vez en una unidad de traducción, pero varias veces en todo el código. ¿Es esto cierto?
Si se incluye solo una vez en todo el código, cuando un archivo desea incluirlo y el preprocesador detecta que ya se ha incluido, ¿cómo sabe ese archivo que desea usarlo el paradero en el código en el que se incluyó anteriormente?
El "preprocesador" realmente inserta un "archivo de encabezado" antes de que comience la compilación. Solo piense que es simplemente "reemplazar" su directiva #include
.
El guardia ...
#ifndef MY_HEADER_H
#define MY_HEADER_H
....
#endif
... se ejecuta después del reemplazo. Por lo tanto, el encabezado puede realmente incluirse varias veces, pero la parte "protegida" del texto solo se pasa al compilador una vez por el preprocesador.
Por lo tanto, si hay definiciones de generación de código en el encabezado, se incluirán, por supuesto, en el archivo objeto de la unidad de compilación (también conocido como "módulo"). Si el mismo encabezado es #include
deded en varios módulos, estos aparecerán varias veces.
Para las definiciones static
, esto no es ningún problema, ya que no serán visibles más allá del módulo (también conocido como alcance del archivo ). Para las definiciones globales del programa, eso es diferente y dará lugar a un error de "definiciones múltiples".
Nota: esto es principalmente para C. Para C ++, existen diferencias significativas, ya que las clases, etc. agregan complejidad adicional a qué / cuándo se permiten múltiples objetos globales.
El archivo de encabezado se incluirá una vez por unidad de traducción, sí. Se puede incluir varias veces por programa, ya que cada unidad de traducción se maneja por separado para el proceso de compilación. Se reúnen durante el proceso de vinculación para formar un programa completo.
Este es el proceso:
source header source header header
/ / / | / /
/ / / | / /
PREPROCESSOR PREPROCESSOR
| |
V V
preprocessed code preprocessed code
| |
COMPILER COMPILER
| |
V V
object code object code
/ /
/ /
/ /
LINKER
|
V
executable
Preprocesamiento
#include
es para este primer paso. Le indica al preprocesador que procese el archivo especificado e inserte el resultado en la salida.
Si A
incluye B
y C
, e B
incluye C
, la salida del preprocesador para A
incluirá el texto procesado de C
dos veces.
Esto es un problema, ya que resultará en declaraciones duplicadas. Un remedio es usar las variables del preprocesador para saber si el código fuente ha sido incluido (también conocido como guardas de encabezado).
#ifndef EXAMPLE_H
#define EXAMPLE_H
// header contents
#endif
La primera vez, EXAMPLE_H
no está definido, y el preprocesador evaluará los contenidos dentro del bloque ifndef
/ endif
. La segunda vez, saltará ese bloque. Así que la salida procesada cambia , y las definiciones se incluyen solo una vez.
Esto es tan común que hay una directiva no estándar implementada por algunos compiladores que es más corta y no requiere elegir una variable de preprocesador única:
#pragma once
// header contents
Puede averiguar cuán portátil quiere su código C / C ++ y qué protector de cabecera usar.
Los guardias de encabezados se asegurarán de que el contenido de cada archivo de encabezado esté presente como máximo una vez en el código preprocesado para una unidad de traducción.
Compilando
El compilador genera código de máquina a partir de su C / C ++ preprocesado.
En general, los archivos de encabezado solo incluyen declaraciones, no las definiciones reales (también conocidas como implementaciones). El compilador incluye una tabla de símbolos para todo lo que actualmente falta una definición.
Enlace
El enlazador combina los archivos objeto. Hace coincidir las definiciones (también conocido como implementaciones) con las referencias a la tabla de símbolos.
Puede ser que dos archivos de objeto proporcionen la definición, y el vinculador tomará uno. Esto sucede si has puesto código ejecutable en tus encabezados. Esto generalmente no ocurre en C, pero ocurre muy frecuentemente en C ++, debido a las plantillas.
El encabezado "código", ya sea declaraciones o definiciones, se incluye varias veces en todos los archivos de objetos, pero el vinculador combina todo eso, de modo que solo está presente una vez en el ejecutable. (Estoy excluyendo funciones en línea, que están presentes varias veces).
Un archivo de encabezado con los guardias de inclusión apropiados se incluirá solo una vez por unidad de traducción . Estrictamente hablando, puede incluirse varias veces, pero las partes entre el preprocesador #ifndef
y #endif
se omitirán en las inclusiones posteriores. Si se hace correctamente, esto debería ser todo (o la mayoría) del archivo.
Una unidad de traducción generalmente corresponde a un "archivo fuente", aunque algunas implementaciones poco claras pueden usar una definición diferente. Si un archivo fuente compilado por separado incluye el mismo encabezado, el preprocesador no tiene forma de saber que otro archivo ya lo incluyó, o que cualquier otro archivo era parte del mismo proyecto.
Tenga en cuenta que cuando conecte varios archivos de origen (unidades de traducción) en un único binario, puede encontrar problemas con varias definiciones si el encabezado no consiste solo en declaraciones, plantillas, definiciones de funciones marcadas en inline
o definiciones de variables estáticas . Para evitar esto, debe declarar funciones en el encabezado y definirlas en un archivo de origen separado, que vinculará junto con sus otros archivos de origen.