Regla 0x500Ch: Prohibición de inclusión directa de archivos de código fuente C (.c)
Compilacion, preprocesador y seguridad (0x50XX)
0x500Ch: Prohibición de inclusión directa de archivos de código fuente C (.c)¶
Enunciado normativo¶
NO DEBE incluirse un archivo
.ccon#include. Para compartir código se incluye la cabecera.hy se compilan todos los.cen el enlace.
¿Por qué existe esta regla?¶
El problema¶
#include "modulo.c" copia el cuerpo completo de un módulo dentro del archivo
que lo incluye, en vez de tratarlo como una unidad de traducción separada. Si
modulo.c ya se compiló por su cuenta, sus funciones quedan definidas dos
veces y el enlazador aborta con «multiple definition». Si no se compiló, el
módulo deja de ser independiente y pierde el propósito de ocultar su estado
interno: las funciones static y las variables de archivo quedan visibles desde
el cliente.
La compilación separada es el mecanismo con el que C estructura programas
grandes: cada .c se compila a un .o y el enlazador los une. Incluir .c
ataca justamente ese mecanismo.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Enlace | multiple definition of 'funcion'; si el .c tiene main, choca con el del cliente. |
| Encapsulamiento | El cliente ve y puede pisar los símbolos static del módulo. |
| Compilación | Cada cambio del .c incluido recompila todo el proyecto. |
| Mantenibilidad | La frontera entre módulos se borra y crecen las dependencias ocultas. |
Fundamento en el estándar y en la cátedra¶
ISO/IEC 9899:2011 §5.1.1.1 describe el modelo de compilación separada y §6.9 las
definiciones externas: cada entidad externa se define una sola vez en todo el
programa. Incluir un .c rompe esa unicidad. La cátedra exige #include sólo
para .h porque el proyecto se compila con gcc main.c modulo.c.
Alcance y excepciones¶
Aplica a cualquier #include cuyo argumento termine en .c. Excepción
legítima: los archivos de X-macros con extensión propia (.def, .inc) que
se incluyen a propósito y no se compilan solos. También puede aparecer #include "impl.c" en pruebas muy acotadas para acceder a funciones static, pero en la
materia se considera una violación.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Incluir el .c en lugar del .h¶
#include "modulo.h"
#include "modulo.c"
int main(void)
{
return modulo_calcular(2);
}Por qué falla: se compila main.c y además modulo.c por separado; modulo
queda definido dos veces y el enlazador falla. El #include "modulo.c" es
también la causa de que el módulo deje de ser reutilizable.
❌ Contraejemplo 2 — Incluir un .c que trae main¶
#include "otro_programa.c"
int main(void)
{
return 0;
}Por qué falla: otro_programa.c contiene su propio main, así que la unidad de
traducción termina con dos definiciones de main y el enlazador aborta. El
código de otro ejecutable nunca se reutiliza por inclusión.
✅ Ejemplo conforme 1 — Cabecera y compilación separada¶
#include "modulo.h"
int main(void)
{
return modulo_calcular(2);
}gcc -std=c11 -Wall -Wextra -Werror -pedantic main.c modulo.c -o appmain.c sólo ve la interfaz declarada en modulo.h; modulo.c se compila
aparte y el enlazador une ambos objetos. Cada módulo conserva su estado interno.
✅ Ejemplo conforme 2 — X-macro con extensión propia¶
static const char *const nombres[] = {
#include "estados.inc"
};estados.inc no es una unidad de traducción: se incluye para generar código y
no se compila por sí solo. Es la excepción prevista; nunca se usa la extensión
.c para este patrón.
⚠️ Casos límite¶
Un solo archivo: si el ejercicio es trivial, igual conviene separar en módulos si crece; no justifica incluir
.c.Funciones
staticen pruebas: acceder a ellas incluyendo el.ces un atajo; la alternativa es exponer una función de prueba o usar#includedel.hcon una interfaz interna.Código generado: puede incluir fragmentos
.inc; documentar la excepción.Extensión engañosa: un archivo
.hque en realidad contiene definiciones también viola el espíritu de la regla; la interfaz va en.hy el cuerpo en.c.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Reporta 0x500Ch ante #include "...c". |
gcc / ld | gcc main.c modulo.c -o app | multiple definition of 'modulo_calcular'. |
| Revisión manual | — | #include con extensión .c en el bloque de includes. |
Checklist de autocontrol¶
¿Todos mis
#includede proyecto apuntan a.h?¿Compilé cada
.cpor separado y los uní en el enlace?¿Evité incluir un
.cpara acceder a funcionesstatic?Si uso X-macros, ¿el archivo incluido no se compila solo y usa otra extensión?
Reglas relacionadas¶
0x0205h: En archivos .c la inclusión de la cabecera propia debe figurar en primer lugar — la interfaz se expone por la cabecera, no copiando el fuente.
0x5011h: Prohibición de declaraciones extern en archivos de implementación (.c) — lo compartido se declara en el
.h, no conexternen el.c.0x5005h: Organizá la estructura de tus archivos .c de forma estándar — la compilación separada presupone archivos con secciones claras.