Anatomía de un archivo .c
Un programa en C suele organizarse en uno o varios archivos .c (código fuente) y, opcionalmente, archivos .h (cabeceras). Aunque el lenguaje es flexible, la mayoría de programas comparten una estructura: directivas del preprocesador, declaraciones (prototipos), definiciones de funciones y, como punto de entrada, la función main.
1) Directivas de preprocesador: #include y compañía
Las líneas que empiezan por # no son “C” propiamente dicho: las procesa el preprocesador antes de compilar. La más común es #include, que inserta el contenido de una cabecera.
#include <stdio.h>: busca en rutas del sistema (biblioteca estándar).#include "mimodulo.h": busca primero en el directorio del proyecto (y luego en rutas del sistema).
Si falta una cabecera, el error suele aparecer en preprocesado/compilación: “file not found” o, más adelante, funciones “implícitamente declaradas” (según el estándar y los warnings).
2) La función main: punto de entrada
El sistema operativo inicia tu programa llamando a main. Formas típicas:
int main(void) { /* ... */ }int main(int argc, char *argv[]) { /* ... */ }La convención es devolver 0 si todo fue bien y un valor distinto de cero si hubo error.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
3) Bloques con llaves, sentencias y punto y coma
Un bloque es un conjunto de sentencias entre { y }. Muchas construcciones (funciones, if, while) introducen bloques.
Una sentencia suele terminar en ; (punto y coma). Ejemplos de sentencias: una asignación, una llamada a función, un return.
int main(void) { /* bloque de la función */
int x = 5; /* sentencia (declaración con inicialización) */
x = x + 1; /* sentencia */
return 0; /* sentencia */
}Errores típicos aquí: olvidar un ; o desbalancear llaves. El compilador suele reportar el problema “más adelante” de donde ocurrió, así que conviene revisar unas líneas antes del punto señalado.
Declaración vs definición (y por qué importa)
En C, declarar es introducir un nombre y su tipo para que el compilador sepa “qué es”. Definir es además proporcionar la implementación o reservar almacenamiento.
Ejemplos con variables
- Declaración (sin reservar almacenamiento, normalmente en cabeceras):
extern int contador; - Definición (reserva almacenamiento, normalmente en un
.c):int contador = 0;
Si defines la misma variable global en dos .c distintos, el enlazador suele fallar con “multiple definition”. Si solo la declaras con extern pero nunca la defines, el enlazador fallará con “undefined reference”.
Ejemplos con funciones
Un prototipo es una declaración de función:
int suma(int a, int b);La definición incluye el cuerpo:
int suma(int a, int b) {
return a + b;
}Si llamas a una función sin declaración visible, con opciones estrictas el compilador lo advertirá o lo tratará como error. Si la declaras pero no la defines en ningún objeto enlazado, el error aparecerá en el enlazado (“undefined reference”).
Pipeline de construcción: de .c a ejecutable
Cuando ejecutas gcc o clang para generar un programa, internamente ocurren etapas. Entenderlas te ayuda a ubicar el origen de los errores.
| Etapa | Entrada | Salida | Errores típicos |
|---|---|---|---|
| Preprocesado | .c + cabeceras | C expandido | fatal error: ... file not found, macros mal formadas |
| Compilación | C preprocesado | Ensamblador (.s) o intermedio | errores de sintaxis, tipos incompatibles, variables no declaradas |
| Ensamblado | .s | Objeto (.o) | raro en C básico; problemas de toolchain |
| Enlazado | .o + librerías | Ejecutable | undefined reference, multiple definition, librerías faltantes |
Cómo ver el resultado de cada etapa
Con gcc/clang puedes detenerte en etapas:
- Solo preprocesar (útil para diagnosticar
#include):gcc -E main.c - Compilar a objeto (sin enlazar):
gcc -c main.c -o main.o - Generar ensamblador:
gcc -S main.c -o main.s
Ejemplo mínimo que compila
Archivo main.c:
#include <stdio.h>
int main(void) {
puts("Hola, C");
return 0;
}Compilación recomendada con warnings (GCC o Clang):
gcc -std=c11 -Wall -Wextra -Wpedantic -O0 -g main.c -o programaNotas prácticas:
-std=c11: fija el estándar (evita comportamientos “por defecto” del compilador).-Wall -Wextra -Wpedantic: activa advertencias útiles; trátalas como señales de problemas reales.-O0 -g: ideal para aprender y depurar (sin optimizaciones, con símbolos de depuración).
Ejemplo con varios archivos: prototipos, objetos y enlazado
Supón dos archivos: uno con la función y otro con main.
Archivo util.h (declaración)
#ifndef UTIL_H
#define UTIL_H
int suma(int a, int b);
#endifArchivo util.c (definición)
#include "util.h"
int suma(int a, int b) {
return a + b;
}Archivo main.c (usa la función)
#include <stdio.h>
#include "util.h"
int main(void) {
printf("%d\n", suma(2, 3));
return 0;
}Compilar paso a paso
gcc -std=c11 -Wall -Wextra -Wpedantic -c util.c -o util.o
gcc -std=c11 -Wall -Wextra -Wpedantic -c main.c -o main.o
gcc util.o main.o -o programaO en una sola línea (el compilador hace el pipeline por ti):
gcc -std=c11 -Wall -Wextra -Wpedantic util.c main.c -o programaErrores comunes y en qué etapa ocurren
1) #include faltante: “file not found”
Ejemplo de mensaje:
main.c:1:10: fatal error: util.h: No such file or directoryCómo corregirlo de forma sistemática:
- Verifica el nombre exacto del archivo (mayúsculas/minúsculas importan en Linux/macOS).
- Si es cabecera del proyecto, usa comillas:
#include "util.h". - Asegura que el archivo esté en el directorio correcto o añade ruta de inclusión:
gcc -Iinclude ...(si tus cabeceras están eninclude/).
2) Símbolo no resuelto en enlazado: “undefined reference”
Ejemplo típico:
/usr/bin/ld: main.o: in function `main':
main.c:(.text+0x15): undefined reference to `suma'
collect2: error: ld returned 1 exit statusInterpretación:
main.ollama asuma, pero el enlazador no encuentra su definición en los objetos/librerías enlazados.
Corrección paso a paso:
- Confirma que
sumaestá definida en algún.c(no solo declarada en un.h). - Asegúrate de compilar y enlazar ese archivo: incluye
util.cen el comando o enlazautil.o. - Verifica coincidencia exacta de firma (nombre y parámetros). Si cambiaste el prototipo en
util.h, recompila todo.
3) Definición múltiple: “multiple definition”
Suele ocurrir cuando pones una definición global en una cabecera incluida por varios .c.
/usr/bin/ld: util.o:(.bss+0x0): multiple definition of `contador'; main.o:(.bss+0x0): first defined hereCorrección:
- En la cabecera: declara con
extern(sin definir). - En un solo
.c: define la variable.
/* en util.h */
extern int contador;
/* en util.c */
int contador = 0;Cómo leer mensajes del compilador sin “probar al azar”
Regla 1: identifica la etapa por el tipo de mensaje
- “
fatal error: ... file not found” suele ser preprocesado. - “
error:expected ...” o “undeclared” suele ser compilación. - “
undefined reference” o “ld:” suele ser enlazado.
Regla 2: empieza por el primer error real
Un error de sintaxis temprano puede generar una cascada de errores secundarios. Corrige el primero que aparece en la salida (el más arriba) y recompila.
Regla 3: usa la ubicación (archivo:línea:columna)
Los compiladores suelen indicar archivo:línea:columna. Ve exactamente a esa posición y revisa también 5–10 líneas antes (por ejemplo, un ; faltante suele “romper” la línea siguiente).
Regla 4: convierte warnings en tareas concretas
Con -Wall -Wextra -Wpedantic verás advertencias como:
warning: unused variable 'x' [-Wunused-variable]No lo ignores: decide si debes usar la variable, eliminarla o marcarla explícitamente (por ejemplo, en parámetros no usados). Mantener el proyecto sin warnings reduce errores reales.
Regla 5: reproduce el problema con el comando exacto
Evita cambiar muchas cosas a la vez. Mantén un comando estable (por ejemplo, el de compilación recomendado) y modifica una sola causa probable. Si usas varios archivos, recompila los que cambiaste y luego enlaza; si dudas, recompila todo para evitar objetos desactualizados.
Comandos típicos (GCC/Clang) recomendados
Compilar un archivo
clang -std=c11 -Wall -Wextra -Wpedantic -O0 -g main.c -o programaCompilar varios archivos
gcc -std=c11 -Wall -Wextra -Wpedantic -O0 -g main.c util.c -o programaSolo compilar a objetos (sin enlazar)
gcc -std=c11 -Wall -Wextra -Wpedantic -O0 -g -c main.c util.cVer el preprocesado para diagnosticar #include
gcc -E main.c > main.iEl archivo main.i resultante te permite comprobar si una cabecera se está insertando como esperas y qué macros están afectando al código.