programa - extend e include diferencias
¿Debo usar#include en los encabezados? (9)
¿Es necesario #include
algún archivo, si dentro de un encabezado (* .h), se usan los tipos definidos en este archivo?
Por ejemplo, si uso GLib y deseo utilizar el tipo básico gchar
en una estructura definida en mi encabezado, ¿es necesario hacer un #include <glib.h>
, sabiendo que ya lo tengo en mi archivo * .c?
Si es así, ¿también tengo que ponerlo entre #ifndef
y #define
o después de #define
?
Debe incluir el encabezado de su encabezado, y no es necesario incluirlo en .c. Incluye debe ir después del #define para que no se incluyan innecesariamente varias veces. Por ejemplo:
/* myHeader.h */
#ifndef MY_HEADER_H
#define MY_HEADER_H
#include <glib.h>
struct S
{
gchar c;
};
#endif /* MY_HEADER_H */
y
/* myCode.c */
#include "myHeader.h"
void myFunction()
{
struct S s;
/* really exciting code goes here */
}
Durante la compilación, el preprocesador simplemente reemplaza la directiva #include por contenido de archivo especificado. Para evitar un bucle infinito, debería usar
#ifndef SOMEIDENTIFIER
#define SOMEIDENTIFIER
....header file body........
#endif
Si se incluyó un encabezado en otro encabezado que se incluyó en su archivo, no es necesario volver a incluirlo explícitamente, ya que se incluirá en el archivo recursivamente.
Las reglas Goddard Space Flight Center ( GSFC ) de la GSFC para los encabezados en C indican que debe ser posible incluir un encabezado en un archivo fuente como el único encabezado y luego se compilará el código que usa las facilidades proporcionadas por ese encabezado.
El beneficio de esta regla es que si alguien necesita usar el encabezado, no tiene que esforzarse para determinar qué otros encabezados también deben incluirse, ellos saben que el encabezado proporciona todo lo necesario.
La posible desventaja es que algunos encabezados se pueden incluir muchas veces; es por eso que los guardias de encabezado de inclusión múltiple son cruciales (y por qué los compiladores intentan evitar la inclusión de encabezados siempre que sea posible).
Implementación
Esta regla significa que si el encabezado usa un tipo - como '' FILE *
'' o '' size_t
'' - entonces debe asegurarse de que se incluya el otro encabezado apropiado ( <stdio.h>
o <stddef.h>
por ejemplo). Un corolario, a menudo olvidado, es que el encabezado no debe incluir ningún otro encabezado que el usuario del paquete no necesite para usar el paquete. El encabezado debe ser mínimo, en otras palabras.
Además, las reglas de GSFC proporcionan una técnica simple para garantizar que esto sea lo que sucede:
- En el archivo de origen que define la funcionalidad, el encabezado debe ser el primer encabezado enumerado.
Por lo tanto, supongamos que tenemos un Magic Sort.
magicsort.h
#ifndef MAGICSORT_H_INCLUDED
#define MAGICSORT_H_INCLUDED
#include <stddef.h>
typedef int (*Comparator)(const void *, const void *);
extern void magicsort(void *array, size_t number, size_t size, Comparator cmp);
#endif /* MAGICSORT_H_INCLUDED */
magicsort.c
#include <magicsort.h>
void magicsort(void *array, size_t number, size_t size, Comparator cmp)
{
...body of sort...
}
Tenga en cuenta que el encabezado debe incluir algún encabezado estándar que defina size_t
; el encabezado estándar más pequeño que lo hace es <stddef.h>
, aunque varios otros también lo hacen ( <stdio.h>
, <stdlib.h>
, <string.h>
, posiblemente algunos otros).
Además, como se mencionó anteriormente, si el archivo de implementación necesita algunos otros encabezados, que así sea, y es completamente normal que se necesiten algunos encabezados adicionales. Pero el archivo de implementación (''magicsort.c'') debe incluirlos y no depender de su encabezado para incluirlos. El encabezado solo debe incluir lo que necesitan los usuarios del software; no lo que necesitan los implementadores
Encabezados de configuración
Si su código usa un encabezado de configuración (GNU Autoconf y el ''config.h'' generado, por ejemplo), puede necesitar usar esto en ''magicsort.c'':
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "magicsort.h"
...
Esta es la única vez que sé que el encabezado privado del módulo no es el primer encabezado en el archivo de implementación. Sin embargo, la inclusión condicional de ''config.h'' probablemente debería estar en ''magicsort.h''.
Actualización 2011-05-01
La URL vinculada anteriormente ya no es funcional (404). Puede encontrar el estándar C ++ (582-2003-004) en EverySpec.com ; el estándar C (582-2000-005) parece faltar en acción.
Las pautas del estándar C fueron:
§2.1 UNIDADES
(1) El código se debe estructurar como unidades o como archivos de encabezado independientes.
(2) Una unidad constará de un único archivo de encabezado (.h) y uno o más archivos de cuerpo (.c). En conjunto, los archivos de encabezado y cuerpo se conocen como archivos de origen.
(3) Un archivo de encabezado de unidad contendrá toda la información pertinente requerida por una unidad de cliente. El cliente de una unidad necesita acceder solo al archivo de encabezado para usar la unidad.
(4) El archivo del encabezado de la unidad debe contener declaraciones #include para todos los demás encabezados requeridos por el encabezado de la unidad. Esto permite a los clientes usar una unidad al incluir un solo archivo de encabezado.
(5) El archivo del cuerpo de la unidad debe contener una declaración #include para el encabezado de la unidad, antes de todas las demás declaraciones #include. Esto permite que el compilador verifique que todas las sentencias #include requeridas estén en el archivo de encabezado.
(6) Un archivo de cuerpo debe contener solo funciones asociadas con una unidad. Un archivo de cuerpo puede no proporcionar implementaciones para las funciones declaradas en diferentes encabezados.
(7) Todas las unidades cliente que usan cualquier parte de una unidad determinada U deberán incluir el archivo de encabezado para la unidad U; esto asegura que solo hay un lugar donde se definen las entidades en la unidad U. Las unidades cliente pueden llamar solo a las funciones definidas en el encabezado de la unidad; no pueden llamar funciones definidas en el cuerpo pero no declaradas en el encabezado. Las unidades cliente no pueden acceder a las variables declaradas en el cuerpo pero no en el encabezado.
Un componente contiene una o más unidades. Por ejemplo, una biblioteca matemática es un componente que contiene unidades múltiples como vector, matriz y cuaternión.
Los archivos de encabezado autónomos no tienen cuerpos asociados; por ejemplo, un encabezado de tipos comunes no declara funciones, por lo que no necesita cuerpo.
Algunas razones para tener múltiples archivos de cuerpo para una unidad:
- Parte del código del cuerpo depende del hardware o del sistema operativo, pero el resto es común.
- Los archivos son demasiado grandes.
- La unidad es un paquete de utilidad común, y algunos proyectos solo usarán algunas de las funciones. Poner cada función en un archivo separado permite que el vinculador excluya las que no se usaron de la imagen final.
§2.1.1 Encabezado incluye justificación
Este estándar requiere que el encabezado de una unidad contenga declaraciones
#include
para todos los demás encabezados requeridos por el encabezado de la unidad. Colocar#include
para el encabezado de la unidad primero en el cuerpo de la unidad permite al compilador verificar que el encabezado contenga todas las declaraciones#include
requeridas.Un diseño alternativo, no permitido por este estándar, no permite declaraciones
#include
en los encabezados; todos los#include
s se hacen en los archivos del cuerpo. Los archivos de cabecera de unidad deben contener sentencias#ifdef
que comprueben que los encabezados necesarios estén incluidos en el orden correcto.Una ventaja del diseño alternativo es que la lista
#include
en el archivo de cuerpo es exactamente la lista de dependencias necesaria en un archivo MAKE, y esta lista es verificada por el compilador. Con el diseño estándar, se debe usar una herramienta para generar la lista de dependencias. Sin embargo, todos los entornos de desarrollo recomendados de sucursal proporcionan dicha herramienta.Una desventaja importante del diseño alternativo es que si la lista de cabecera requerida de una unidad cambia, cada archivo que use esa unidad debe ser editado para actualizar la lista de enunciados
#include
. Además, la lista de encabezado requerida para una unidad de biblioteca de compilación puede ser diferente en objetivos diferentes.Otra desventaja del diseño alternativo es que los archivos de encabezado de la biblioteca del compilador y otros archivos de terceros deben modificarse para agregar las declaraciones
#ifdef
requeridas.Una práctica común diferente es incluir todos los archivos de encabezado del sistema antes de cualquier archivo de encabezado de proyecto, en los archivos de cuerpo. Este estándar no sigue esta práctica, porque algunos archivos de encabezado de proyecto pueden depender de los archivos de encabezado del sistema, ya sea porque usan las definiciones en el encabezado del sistema o porque quieren anular una definición del sistema. Dichos archivos de encabezado de proyecto deben contener sentencias
#include
para los encabezados del sistema; si el cuerpo los incluye primero, el compilador no verifica esto.
Estándar GSFC disponible a través de Internet Archive 2012-12-10
Information cortesía de Eric S. Bullington :
El estándar de codificación NASA C mencionado se puede acceder y descargar a través del archivo de Internet:
Secuenciando
La pregunta también pregunta:
En caso afirmativo, también tengo que ponerlo (las líneas de
#include
) entre#ifndef
y#define
o después de#define
.
La respuesta muestra el mecanismo correcto: las inclusiones anidadas, etc., deben estar después de #define
(y #define
debería ser la segunda línea que no incluye comentarios en el encabezado), pero no explica por qué es correcto.
Considere lo que sucede si coloca el #include
entre #ifndef
y #define
. Supongamos que el otro encabezado incluye varios encabezados, quizás incluso #include "magicsort.h"
indirectamente. Si la segunda inclusión de magicsort.h
ocurre antes de #define MAGICSORT_H_INCLUDED
, el encabezado se incluirá una segunda vez antes de definir los tipos que define. Por lo tanto, en C89 y C99, cualquier nombre de tipo typedef
se redefinirá erróneamente (C2011 permite que se redefinan al mismo tipo), y obtendrá la sobrecarga de procesar el archivo varias veces, frustrando el propósito del protector de encabezado en el primer lugar. Esta es también la razón por la cual #define
es la segunda línea y no se escribe justo antes del #endif
. La fórmula dada es confiable:
#ifndef HEADERGUARDMACRO
#define HEADERGUARDMACRO
...original content of header — other #include lines, etc...
#endif /* HEADERGUARDMACRO */
Lo que normalmente hago es crear un solo archivo de inclusión que incluya todas las dependencias necesarias en el orden correcto. Entonces podría tener:
#ifndef _PROJECT_H_
#define _PROJECT_H_
#include <someDependency.h>
#include "my_project_types.h"
#include "my_project_implementation_prototypes.h"
#endif
Todo en project.h. Ahora project.h se puede incluir en cualquier lugar sin requisitos de pedido, pero todavía puedo darme el lujo de tener mis dependencias, tipos y prototipos de función API en diferentes encabezados.
Sí, es necesario o el compilador se quejará cuando intente compilar código que no "conoce". Piense en # include''s que son una sugerencia / codazo / codazo al compilador para indicarle que recoja las declaraciones, estructuras, etc., para una compilación exitosa. El truco del encabezado # ifdef / # endif, tal como lo señala jldupont, es acelerar la compilación del código.
Se usa en instancias donde tiene un compilador C ++ y compila código C simple como se muestra aquí. Aquí hay un ejemplo del truco:
#ifndef __MY_HEADER_H__ #define __MY_HEADER_H__ #ifdef __cplusplus extern "C" { #endif /* C code here such as structures, declarations etc. */ #ifdef __cplusplus } #endif #endif /* __MY_HEADER_H__ */
Ahora, si esto se incluyó varias veces, el compilador solo lo incluirá una vez ya que el símbolo __MY_HEADER_H__
se define una vez, lo que acelera los tiempos de compilación. Observe el símbolo cplusplus en el ejemplo anterior, que es la forma estándar normal de hacer frente a la compilación de C ++ si tiene un código C por ahí.
He incluido lo anterior para mostrar esto (a pesar de que no es realmente relevante para la pregunta original del póster). Espero que esto ayude, Saludos, Tom.
PD: Perdón por permitir que alguien rechace esto porque pensé que sería útil para los recién llegados a C / C ++. Deja un comentario / crítica, etc. ya que son bienvenidos.
Simplemente incluya todos los encabezados externos en un archivo de encabezado común en su proyecto, por ejemplo, global.h , e inclúyalo en todos sus archivos c:
Puede verse así:
#ifndef GLOBAL_GUARD
#define GLOBAL_GUARD
#include <glib.h>
/*...*/
typedef int YOUR_INT_TYPE;
typedef char YOUR_CHAR_TYPE;
/*...*/
#endif
Este archivo usa include guard para evitar inclusiones múltiples, definiciones múltiples ilegales, etc.
Una buena práctica es incluir #includes en un archivo de inclusión si el archivo de inclusión lo necesita. Si las definiciones en un archivo de inclusión dado solo se utilizan en el archivo .c, inclúyalo solo en el archivo .c.
En tu caso, lo incluiría en el archivo de inclusión entre # ifdef / # endif.
Esto minimizará las dependencias de modo que los archivos que no necesiten una inclusión determinada no tendrán que ser recompilados si el archivo de inclusión cambia.
Uso la siguiente construcción para estar seguro de que el archivo de inclusión necesario está incluido antes de este include. Incluyo todos los archivos de encabezado solo en los archivos fuente.
#ifndef INCLUDE_FILE_H
#error "#include INCLUDE.h" must appear in source files before "#include THISFILE.h"
#endif
Usualmente, los desarrolladores de bibliotecas protegen sus inclusiones de múltiples incluyendo el #ifndef / # define / #endif "truco" para que no tenga que hacerlo.
Por supuesto, deberías comprobar ... pero de todos modos el compilador te dirá en algún momento ;-) De todos modos, es una buena práctica verificar inclusiones múltiples, ya que ralentiza el ciclo de compilación.