c include header rules-of-thumb

Cómo estructurar#includes en C



header rules-of-thumb (6)

Supongamos que tengo un programa C que está roto en un conjunto de archivos * .c y * .h. Si el código de un archivo usa funciones de otro archivo, ¿dónde debería incluir el archivo de encabezado? Dentro del archivo * .c que usó la función, o dentro del encabezado de ese archivo?

Por ejemplo, el archivo foo.c incluye foo.h , que contiene todas las declaraciones de foo.c ; lo mismo para bar.c y bar.h La función foo1() dentro de foo.c llama a bar1() , que se declara en bar.h y se define en bar.c Ahora la pregunta es, ¿debería incluir bar.h dentro de foo.h , o dentro de foo.c ?

¿Cuál sería un buen conjunto de reglas empíricas para tales asuntos?


Como han señalado otros, un encabezado foo.h debe declarar la información necesaria para poder usar las facilidades provistas por un archivo fuente foo.c. Esto incluiría los tipos, enumeraciones y funciones proporcionadas por foo.c. (No usas variables globales, ¿verdad? Si lo haces, entonces esas también se declaran en foo.h).

El encabezado foo.h debe ser autónomo e idempotente. Autónomo significa que cualquier usuario puede incluir foo.h y no tener que preocuparse sobre qué otros encabezados pueden ser necesarios (porque foo.h incluye esos encabezados). Idempotente significa que si el encabezado está incluido más de una vez, no se produce ningún daño. Eso se logra mediante la técnica clásica:

#ifndef FOO_H_INCLUDED #define FOO_H_INCLUDED ...rest of the contents of foo.h... #endif /* FOO_H_INCLUDED */

La pregunta hecha

El archivo foo.c incluye foo.h, que contiene todas las declaraciones de foo.c; lo mismo para bar.c y bar.h. La función foo1 () dentro de foo.c llama a bar1 (), que se declara en bar.h y se define en bar.c. Ahora la pregunta es, ¿debería incluir bar.h dentro de foo.h, o dentro de foo.c?

Dependerá de si los servicios provistos por foo.h dependen de bar.h o no. Si otros archivos que usan foo.h necesitarán uno de los tipos o enumeraciones definidos por bar.h para usar la funcionalidad de foo.h, entonces foo.h debería asegurarse de que se incluya bar.h (incluyéndolo). Sin embargo, si los servicios de bar.h solo se usan en foo.c y no son necesarios para quienes usan foo.h, entonces foo.h no debe incluir bar.h


El archivo .h debe definir la interfaz pública (también conocida como api) para las funciones en el archivo .c.

Si la interfaz de file1 usa la interfaz de file2, entonces #include file2.h en file1.h

Si la implementación en file1.c hace uso de cosas en file2.c entonces file1.c debería #include file2.h.

Debo admitir eso, porque siempre #incluyo archivo1.h en el archivo1.c - normalmente no me molestaría #incluyendo archivo2.h directamente en file1.c si ya estaba #incluido en file1.h

Si alguna vez se encuentra en la situación en que dos archivos .c #incluyen los archivos .h, es señal de que la modularidad se ha roto y debe pensar en reestructurar un poco las cosas.


Incluyo el conjunto de encabezados más mínimo posible en el archivo .h e incluyo el resto en el archivo .c . Esto tiene el beneficio de reducir tiempos de compilación. Dado su ejemplo, si foo.h realmente no necesita bar.h pero lo incluye de todos modos, y algún otro archivo incluye foo.h , entonces ese archivo se volverá a compilar si cambia bar.h , aunque en realidad no necesite o use bar.h


Solo incluiría archivos de encabezado en un archivo * .h que fuera necesario para el archivo de encabezado. Los archivos de encabezado que se necesitan para un archivo de origen, en mi opinión, deben incluirse en el archivo de origen para que las dependencias sean obvias desde la fuente. Los archivos de encabezado se deben compilar para manejar la inclusión múltiple para que pueda ponerlos en ambos si es necesario para mayor claridad.


Debes incluir foo.h dentro de foo.c. De esta forma, otros archivos c que incluyen foo.h no llevarán bar.h innecesariamente. Este es mi consejo para incluir archivos de encabezado:

  • Agregue definiciones de inclusión en los archivos c: de esta manera, las dependencias de archivos son más obvias al leer el código.
  • Divida el foo.h en dos archivos separados, digamos foo_int.h y foo.h. El primero lleva el tipo y las declaraciones de reenvío necesarias solo por foo.c. El foo.h incluye las funciones y los tipos que necesitan los módulos externos. Esto es algo así como la sección privada y pública de foo.
  • Evite las referencias cruzadas, es decir, referencias foo bar y referencias de barras foo. Esto puede causar problemas de vinculación y también es un signo de mal diseño

Usando los ejemplos de foo.c y foo.h , he encontrado estas pautas útiles:

  • Recuerde que el propósito de foo.h es facilitar el uso de foo.c , así que manténgalo lo más simple, organizado y autoexplicativo posible. Sea liberal con comentarios que expliquen cómo y cuándo usar las características de foo.c , y cuándo no usarlas.

  • foo.h declara las características públicas de foo.c : functions, macros, typedefs, y ( shudder ) variables globales.

  • foo.c debe #include "foo.h - ver discusión, y también el comentario de Jonathan Leffler a continuación.

  • Si foo.c requiere encabezados adicionales para que compile, foo.c en foo.c

  • Si se requieren encabezados externos para que foo.h compile, foo.h en foo.h

  • Aproveche el preprocesador para evitar que foo.h se incluya más de una vez. (Vea abajo.)

  • Si por algún motivo se requiere un encabezado externo para que otro archivo .c use las características en foo.c , incluya el encabezado en foo.h para evitar que el próximo desarrollador se foo.h innecesariamente. Si es reacio a esto, considere agregar macro que mostrará instrucciones en tiempo de compilación si los encabezados requeridos no se han incluido.

  • No incluya un archivo .c dentro de otro archivo .c menos que tenga un buen motivo y lo documente claramente.

Como señaló kgiannakakis, es útil separar la interfaz pública de las definiciones y declaraciones necesarias solo dentro de foo.c Pero en lugar de crear dos archivos, a veces es mejor dejar que el preprocesador lo haga por usted:

// foo.c #define _foo_c_ // Tell foo.h it''s being included from foo.c #include "foo.h" . . .

// foo.h #if !defined(_foo_h_) // Prevent multiple inclusion #define _foo_h_ // This section is used only internally to foo.c #ifdef _foo_c_ . . . #endif // Public interface continues to end of file. #endif // _foo_h_ // Last-ish line in foo.h