¿Incluyendo un archivo fuente C en otro?

111

¿Está bien (o incluso se recomienda / una buena práctica) para #includeun .carchivo en otro .carchivo?

SS Anne
fuente
4
Me tomé la libertad de editar la pregunta y también apliqué algunas etiquetas útiles. Algunas de mis etiquetas pueden mostrar un sesgo, je.
relajarse

Respuestas:

113

Si se usa correctamente, esta puede ser una técnica útil.

Supongamos que tiene un subsistema complejo y de rendimiento crítico con una interfaz pública bastante pequeña y una gran cantidad de código de implementación no reutilizable. El código se ejecuta en varios miles de líneas, un centenar de funciones privadas y bastante información privada. Si trabaja con sistemas integrados no triviales, probablemente se enfrente a esta situación con la suficiente frecuencia.

Su solución probablemente será en capas, modular y desacoplada y estos aspectos se pueden representar y reforzar de manera útil codificando diferentes partes del subsistema en diferentes archivos.

Con C, puede perder mucho al hacer esto. Casi todas las cadenas de herramientas proporcionan una optimización decente para una sola unidad de compilación, pero son muy pesimistas acerca de cualquier cosa declarada externa.

Si pones todo en un módulo fuente C, obtienes:

  • Mejoras en el rendimiento y el tamaño del código: las llamadas a funciones se incluirán en muchos casos. Incluso sin la inserción, el compilador tiene oportunidades para producir código más eficiente.

  • Ocultación de funciones y datos de nivel de enlace.

  • Evitar la contaminación del espacio de nombres y su corolario: puede usar nombres menos difíciles de manejar.

  • Recopilación y vinculación más rápidas.

Pero también obtiene un lío profano cuando se trata de editar este archivo y pierde la modularidad implícita. Esto se puede solucionar dividiendo la fuente en varios archivos e incluyéndolos para producir una sola unidad de compilación.

Sin embargo, debe imponer algunas convenciones para administrar esto correctamente. Estos dependerán de su cadena de herramientas hasta cierto punto, pero algunos consejos generales son:

  • Coloque la interfaz pública en un archivo de encabezado separado; debería hacerlo de todos modos.

  • Tenga un archivo .c principal que incluya todos los archivos .c subsidiarios. Esto también podría incluir el código de la interfaz pública.

  • Utilice protecciones del compilador para asegurarse de que las unidades de compilación externas no incluyan encabezados privados y módulos fuente.

  • Todos los datos y funciones privados deben declararse estáticos.

  • Mantenga la distinción conceptual entre archivos .cy .h. Esto aprovecha las convenciones existentes. La diferencia es que tendrá muchas declaraciones estáticas en sus encabezados.

  • Si su cadena de herramientas no impone ningún motivo para no hacerlo, asigne un nombre a los archivos de implementación privados como .cy .h. Si usa protectores de inclusión, estos no producirán código ni introducirán nombres nuevos (puede terminar con algunos segmentos vacíos durante el enlace). La gran ventaja es que otras herramientas (por ejemplo, IDE) tratarán estos archivos de manera adecuada.


fuente
1
+1 esta sigue siendo la realidad, mientras que mejores compiladores harán que este método quede obsoleto con el tiempo. GCC 4.5 con optimización del tiempo de enlace es un gran paso en el camino.
u0b34a0f6ae
He visto tantos programas hacer esto, y me pone de los nervios cuando intento reutilizar su código. Gracias por explicar por qué lo hacen y sugerir el uso de un protector (que a menudo no se hace).
Joey Adams
En el desarrollo integrado para C51, el uso de archivos C externos y múltiples no ha causado más que dolores de cabeza. Estoy cambiando a tener un archivo C que incluya todos los demás para recuperar mi tiempo.
mahesh
Para los registros: GCC admite la optimización en diferentes unidades de traducción, consulte este hilo SO y la sección del manual de GCC sobre la optimización del tiempo de enlace .
Twonky
60

¿está bien? sí, se compilará

es recomendado no: los archivos .c se compilan en archivos .obj, que se vinculan entre sí después de la compilación (por el vinculador) en el ejecutable (o biblioteca), por lo que no es necesario incluir un archivo .c en otro. Lo que probablemente quiera hacer en su lugar es crear un archivo .h que enumere las funciones / variables disponibles en el otro archivo .c e incluir el archivo .h

Steven A. Lowe
fuente
14
También vale la pena señalar que incluso si se compila, es posible que no se vincule si el archivo .c # incluido también se compila y los dos archivos de objeto están vinculados entre sí; podría terminar con símbolos definidos múltiples.
Nick Meyer
1
Una pequeña pregunta. Tengo un archivo de encabezado que declara un método struct + y también el archivo .c correspondiente para definirlos. Si ese método toma la estructura como parámetro, ¿cómo evitaría incluir el archivo .c en otro archivo .c donde está definido el método principal?
stdout
12

No.

Dependiendo de su entorno de compilación (no lo especifica), puede encontrar que funciona exactamente de la manera que desea.

Sin embargo, hay muchos entornos (tanto IDE como muchos Makefiles hechos a mano) que esperan compilar * .c; si eso sucede, probablemente terminará con errores de vinculador debido a símbolos duplicados.

Por regla general, esta práctica debe evitarse.

Si es absolutamente necesario #incluir la fuente (y generalmente debe evitarse), use un sufijo de archivo diferente para el archivo.

Andrew Edgecombe
fuente
9

Pensé en compartir una situación en la que mi equipo decidió incluir archivos .c. Nuestra arquitectura consta en gran parte de módulos desacoplados mediante un sistema de mensajes. Estos controladores de mensajes son públicos y llaman a muchas funciones de trabajadores estáticos locales para que hagan su trabajo. El problema surgió al intentar obtener cobertura para nuestros casos de prueba unitaria, ya que la única forma de ejercitar este código de implementación privado era indirectamente a través de la interfaz de mensajes públicos. Con algunas funciones de los trabajadores hasta las rodillas en la pila, esto resultó ser una pesadilla para lograr una cobertura adecuada.

La inclusión de los archivos .c nos dio una forma de llegar al engranaje de la máquina que estábamos interesados ​​en probar.

Matthew Alford
fuente
6

Puede usar el compilador gcc en linux para vincular dos archivos c en una salida. Suponga que tiene dos archivos c, uno es 'main.c' y otro es 'support.c'. Entonces, el comando para vincular estos dos es

gcc main.c support.c -o main.out

De este modo, dos archivos se vincularán a una única salida main.out Para ejecutar la salida, el comando será

./main.out

Si está usando la función en main.c que está declarada en el archivo support.c, entonces debe declararla en main también usando la clase de almacenamiento extern.

sumitroy
fuente
5

La extensión del archivo no es importante para la mayoría de los compiladores de C, por lo que funcionará.

Sin embargo, dependiendo de la configuración de su archivo MAKE o proyecto, el archivo c incluido puede generar un archivo de objeto separado. Al vincular, eso puede conducir a símbolos de doble definición.

HS.
fuente
3

Puede incluir correctamente archivos .C o .CPP en otros archivos de origen. Dependiendo de su IDE, generalmente puede evitar el doble enlace mirando las propiedades de los archivos de origen que desea incluir, generalmente haciendo clic derecho sobre él y haciendo clic en propiedades, y desmarcar / marcar compilar / vincular / excluir de la compilación o cualquier opción tal vez. O no podría incluir el archivo en el proyecto en sí, por lo que el IDE ni siquiera sabrá que existe y no intentará compilarlo. Y con los archivos MAKE simplemente no pondría el archivo en él para compilar y vincular.

EDITAR: Lo siento, hice una respuesta en lugar de una respuesta a otras respuestas :(

MikeyPro
fuente
1

El lenguaje C no prohíbe ese tipo de #include, pero la unidad de traducción resultante aún debe ser C.

No sé qué programa estás usando con un archivo .prj. Si está usando algo como "make" o Visual Studio o lo que sea, solo asegúrese de configurar su lista de archivos para que se compilen sin el que no se puede compilar de forma independiente.

Programador de Windows
fuente
0

La inclusión de un archivo C en otro archivo es legal, pero no es recomendable, a menos que sepa exactamente por qué está haciendo esto y qué está tratando de lograr.
Estoy casi seguro de que si publicas aquí la razón por la que detrás de tu pregunta la comunidad te encontrará otra forma más apropiada de lograr tu objetivo (por favor, ten en cuenta el "casi", ya que es posible que esta sea la solución dado el contexto ).

Por cierto, me perdí la segunda parte de la pregunta. Si el archivo C se incluye en otro archivo y al mismo tiempo se incluye en el proyecto, probablemente terminará con un problema de símbolo duplicado por qué vincular los objetos, es decir, la misma función se definirá dos veces (a menos que todos sean estáticos).

Ilya
fuente
-6

deberías agregar un encabezado como este

#include <another.c>

nota: ambos archivos deben colocarse en el mismo lugar

Lo uso en codevison AVR para microcontroladores ATMEGA y trabajo realmente, pero no funciona en un archivo de lenguaje C normal

milad
fuente