¿Dónde poner incluir declaraciones, encabezado o fuente?

106

¿Debo poner las inclusiones en el archivo de encabezado o en el archivo de origen? Si el archivo de encabezado contiene las declaraciones de inclusión, si incluyo ese archivo de encabezado en mi fuente, ¿mi archivo de origen tendrá todos los archivos incluidos que estaban en mi encabezado? ¿O debería incluirlos solo en mi archivo fuente?

Mohit Deshpande
fuente
2
Muchos duplicados anteriores en SO, por ejemplo, dónde se debe poner "incluir" en C ++
Paul R
1
posible duplicado de ¿Debo usar #include en los encabezados?
schot

Respuestas:

141

Solo coloque inclusiones en un encabezado si el encabezado mismo los necesita.

Ejemplos:

  • Tu función devuelve el tipo size_t. Luego #include <stddef.h>en el archivo de encabezado .
  • Tu función usa strlen. Luego #include <string.h>en el archivo fuente .
schot
fuente
2
¿Qué pasa si mi función toma un argumento de tipo size_t?
andrybak
La misma pregunta se expande a c ++: ¿qué pasa si mi estructura / clase tiene un campo / miembro de tipo size_to std::string?
andrybak
10
¿Cuál es la justificación?
Patrizio Bertoni
Tengo una situación cableada, la clase A de C ++ tiene un objeto de otra clase B, y no puedo usar la declaración de avance de B y terminar incluyendo el encabezado B dentro del encabezado A. (el uso del puntero no tiene este problema)
shuva
@andrybak Sus archivos de origen deben incluir su archivo de encabezado para que cualquiera que incluya su encabezado obtenga su fuente también.
Jeremy Trifilo
27

Ha habido bastante desacuerdo sobre esto a lo largo de los años. En un momento, era tradicional que un encabezado solo declarara lo que estaba en cualquier módulo con el que estaba relacionado, por lo que muchos encabezados tenían requisitos específicos que le #includepermitían un cierto conjunto de encabezados (en un orden específico). Algunos programadores de C extremadamente tradicionales todavía siguen este modelo (religiosamente, al menos en algunos casos).

Más recientemente, ha habido un movimiento para hacer que la mayoría de los encabezados sean independientes. Si ese encabezado requiere algo más, el encabezado mismo se encarga de eso, asegurándose de que se incluya todo lo que necesite (en el orden correcto, si hay problemas de pedido). Personalmente, prefiero esto, especialmente cuando el orden de los encabezados puede ser importante, resuelve el problema una vez, en lugar de requerir que todos los que lo usan resuelvan el problema una vez más.

Tenga en cuenta que la mayoría de los encabezados solo deben contener declaraciones. Esto significa que agregar un encabezado innecesario no debería (normalmente) tener ningún efecto en su ejecutable final. Lo peor que pasa es que ralentiza un poco la compilación.

Jerry Coffin
fuente
2
Si todos los encabezados están escritos en el segundo estilo, no debería haber ningún problema de orden. Tener problemas de orden en los encabezados generalmente significa que no incluyó todo lo que necesita en el encabezado.
Adiós SE
12

Sus correos #includeelectrónicos deben ser archivos de encabezado, y cada archivo (fuente o encabezado) debe tener #includelos archivos de encabezado que necesita. Los archivos de encabezado deben tener #includelos archivos de encabezado mínimos necesarios, y los archivos de origen también deben, aunque no es tan importante para los archivos de origen.

El archivo de origen tendrá los encabezados #include, y los encabezados #include, y así sucesivamente hasta la profundidad máxima de anidamiento. Esta es la razón por la que no desea mensajes de correo #includeelectrónico superfluos en los archivos de encabezado: pueden hacer que un archivo de origen incluya muchos archivos de encabezado que puede que no necesite, lo que ralentiza la compilación.

Esto significa que es muy posible que los archivos de encabezado se incluyan dos veces y eso puede ser un problema. El método tradicional es poner "incluir guardias" en los archivos de encabezado, como este para el archivo foo.h:

#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif
David Thornley
fuente
Sé que esta respuesta es muy antigua, pero desde entonces agregaron #pragma una vez, por lo que no debe incluir #ifndef al declarar #include que publiqué esto porque los hilos más antiguos pero más populares / con votos positivos tienden a estar en la parte superior de las búsquedas de Google
Dogunbound hounds
6

El enfoque en el que he evolucionado durante más de veinte años es este;

Considere una biblioteca.

Hay varios archivos C, un archivo H interno y un archivo H externo. Los archivos C incluyen el archivo H interno. El archivo H interno incluye el archivo H externo.

Verá que desde el punto de vista de los compiladores, a medida que compila un archivo C, hay una jerarquía;

externo -> interno -> código C

Este es el orden correcto, ya que lo externo es todo lo que un tercero necesita para usar la biblioteca. Lo que es interno es necesario para compilar el código C.


fuente
4

Si un archivo de cabecera #includesarchivos de cabecera B y C, entonces cada archivo de origen que #includesA será también obtener B y C #included. El preprocesador, literalmente, simplemente realiza la sustitución de texto: en cualquier lugar donde encuentre texto que dice que #include <foo.h>lo reemplaza con el texto del foo.harchivo.

Hay diferentes opiniones sobre si debe colocar #includesencabezados o archivos fuente. Personalmente, prefiero poner todo #includesen el archivo de origen de forma predeterminada, pero cualquier archivo de encabezado que no se pueda compilar sin otros encabezados de requisitos previos debe #includeincluir esos encabezados.

Y cada archivo de encabezado debe contener una protección de inclusión para evitar que se incluya varias veces.

Vicky
fuente
4

En algunos entornos, la compilación será más rápida si solo se incluyen los archivos de encabezado que se necesitan. En otros entornos, la compilación se optimizará si todos los archivos de origen pueden usar la misma colección primaria de encabezados (algunos archivos pueden tener encabezados adicionales más allá del subconjunto común). Idealmente, los encabezados deben construirse para que múltiples operaciones #include no tengan ningún efecto. Puede ser bueno rodear las declaraciones #include con comprobaciones de la protección de inclusión del archivo a incluir, aunque eso crea una dependencia en el formato de esa protección. Además, dependiendo del comportamiento de almacenamiento en caché de archivos de un sistema, un #include innecesario cuyo objetivo termine siendo completamente # ifdef'ed away puede no tardar mucho.

Otra cosa a considerar es que si una función toma un puntero a una estructura, se puede escribir el prototipo como

void foo (struct BAR_s * bar);

sin una definición de BAR_s que tenga que estar dentro del alcance. Un enfoque muy útil para evitar inclusiones innecesarias.

PD: en muchos de mis proyectos, habrá un archivo que se espera que cada módulo #incluya, que contiene cosas como typedefs para tamaños enteros y algunas estructuras y uniones comunes [p. Ej.

typedef union {
  unsigned long l;
  lw corto sin firmar [2];
  carácter sin firmar lb [4];
} U_QUAD;

(Sí, sé que tendría problemas si me cambiara a una arquitectura big-endian, pero dado que mi compilador no permite estructuras anónimas en uniones, el uso de identificadores con nombre para los bytes dentro de la unión requeriría que se acceda a ellos como theUnion.b.b1 etc.que parece bastante molesto.

Super gato
fuente
3

Cree todos sus archivos para que se puedan construir utilizando solo lo que incluyen. Si no necesita una inclusión en su encabezado, elimínela. En un gran proyecto, si no mantiene esta disciplina, se deja abierto a romper una compilación completa cuando alguien elimina una inclusión de un archivo de encabezado que está siendo utilizado por un consumidor de ese archivo y ni siquiera por el encabezado.

repetición
fuente
1

Su archivo fuente tendrá las declaraciones de inclusión si las coloca en el encabezado. Sin embargo, en algunos casos sería mejor ponerlos en el archivo fuente.

Recuerde que si incluye ese encabezado en cualquier otra fuente, también obtendrán las inclusiones del encabezado, y eso no siempre es deseable. Solo debe incluir cosas donde se use.

Splashdust
fuente
1

Solo debe incluir archivos en su encabezado que necesite para declarar constantes y declaraciones de funciones. Técnicamente, estas inclusiones también se incluirán en su archivo fuente, pero en aras de la claridad, solo debe incluir en cada archivo los archivos que realmente necesita usar. También debe protegerlos en su encabezado de la inclusión múltiple de la siguiente manera:

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

Esto evita que el encabezado se incluya varias veces, lo que da como resultado un error del compilador.

JRam930
fuente