c - name - meta tags ejemplos
¿Dónde poner declaraciones, encabezado o fuente? (9)
¿Debo incluir las inclusiones en el archivo de encabezado o en el archivo de origen? Si el archivo de encabezado contiene las instrucciones de inclusión, entonces, si incluyo ese archivo de encabezado en mi fuente, ¿tendrá mi archivo fuente todos los archivos incluidos en mi encabezado? ¿O debería simplemente incluirlos solo en mi archivo fuente?
El enfoque en el que he evolucionado en más de veinte años es este;
Considera 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.
Usted ve que desde el punto de vista de compiladores, ya que compila un archivo C, hay una jerarquía;
externo -> interno -> código C
Este es el orden correcto, ya que lo que es externo es todo lo que un tercero necesita para usar la biblioteca. Lo que es interno se requiere para compilar el código C.
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 deberían construirse de modo que las operaciones múltiples #include no tendrán ningún efecto. Puede ser bueno rodear las sentencias #include con comprobaciones del include-guard del archivo que se incluirá, aunque eso crea una dependencia del formato de ese guardiá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 lejos puede tardar mucho.
Otra cosa a considerar es que si una función toma un puntero a una estructura, uno puede escribir el prototipo como
void foo(struct BAR_s *bar);
sin una definición para BAR_s que tiene que estar en el alcance. Un enfoque muy útil para evitar incluye innecesarios.
PD: en muchos de mis proyectos, habrá un archivo que se espera que cada módulo incluya #, que contenga elementos como typedefs para tamaños enteros y algunas estructuras y uniones comunes [por ej.
typedef union { unsigned long l; unsigned short lw[2]; unsigned char lb[4]; } U_QUAD;
(Sí, sé que estaría en problemas si me mudara a una arquitectura de big-endian, pero como 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.
Ha habido bastante desacuerdo sobre esto a lo largo de los años. En un momento dado, era tradicional que un encabezado solo declarara lo que estaba en el módulo con el que estaba relacionado, por lo que muchos encabezados tenían requisitos específicos para que #include
un determinado conjunto de encabezados (en un orden específico). Algunos programadores de C extremadamente tradicionales aún siguen este modelo (religiosamente, al menos en algunos casos).
Más recientemente, hay un movimiento para hacer que la mayoría de los encabezados sean independientes. Si ese encabezado requiere algo más, el encabezado se encarga de eso, asegurando que todo lo que necesita esté incluido (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 el ejecutable final. Lo peor que ocurre es que ralentiza la compilación un poco.
Haga todos sus archivos para que puedan compilarse usando solo lo que incluyen. Si no necesita incluir en su encabezado elimínelo. En un proyecto grande, si no mantiene esta disciplina, se queda 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.
Si el archivo de cabecera A #includes
incluye los archivos de encabezado B y C, entonces cada archivo de origen que #includes
A también obtendrá B y C #included
. El preprocesador, literalmente, solo realiza la sustitución de texto: en cualquier lugar encuentra texto que dice #include <foo.h>
lo reemplaza con el texto del archivo foo.h
Hay diferentes opiniones sobre si debe poner #includes
en encabezados o archivos fuente. Personalmente, prefiero incluir todos los #includes
en el archivo de origen de forma predeterminada, pero los archivos de encabezado que no se pueden compilar sin otros encabezados de requisito previo deben #include
esos encabezados.
Y cada archivo de encabezado debe contener un protector de inclusión para evitar que se incluya varias veces.
Solo debe incluir archivos en su encabezado que necesite declarar constantes y declaraciones de funciones. Técnicamente, estos includes también se incluirán en su archivo de origen, pero para mayor claridad, solo debe incluir en cada archivo los archivos que realmente necesita usar. También debe protegerlos en su encabezado de inclusión múltiple así:
#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 genera un error de compilación.
Solo poner incluye en un encabezado si el encabezado mismo los necesita.
Ejemplos:
- Tu función devuelve type
size_t
. Luego#include <stddef.h>
en el archivo de encabezado . - Tu función usa
strlen
. Luego#include <string.h>
en el archivo fuente .
Su #include
debe ser de archivos de encabezado, y cada archivo (fuente o encabezado) debe #include
los archivos de encabezado que necesita. Los archivos de encabezado deben #include
los archivos de encabezado mínimos necesarios, y también los archivos de origen, aunque no es tan importante para los archivos fuente.
El archivo de origen tendrá los encabezados que #include
, y los encabezados que #include
, y así sucesivamente hasta la profundidad máxima de anidamiento. Esta es la razón por la que no desea #include
superfluos en los archivos de encabezado: pueden hacer que un archivo fuente incluya muchos archivos de encabezado que puede no necesitar, ralentizando la compilación.
Esto significa que es completamente 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 archivos de encabezado, como este para el archivo foo.h:
#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif
Su archivo fuente tendrá las declaraciones include si lo coloca en el encabezado. Sin embargo, en algunos casos, sería mejor colocarlos en el archivo fuente.
Recuerde que si incluye ese encabezado en cualquier otra fuente, también obtendrá las inclusiones desde el encabezado, y eso no siempre es deseable. Solo debe incluir cosas donde se usa.