Está en la página 1de 7

La compilacin y los mdulos en C y C++

Junio 2014
El objetivo de este articulo es el de presentar los fundamentos de la compilacin en C, C++ y de
la programacin modular.
Esto nos permitir comprender mejor los mensajes de error del compilador. Las nociones
abordadas aqu son independientes del sistema operativo utilizado (Windows o Linux). No
obstante, supondremos que estamos bajo Linux para ver con ms detalle lo que ocurre durante
la compilacin de un programa en C o C++.
Introduccin
Instalacin de un compilador C y C++
Bajo Linux
Bajo Windows
Las fases de compilacin
1) El preprocesado
2) La compilacin
3) El enlazado
Warning y errores
Las grandes etapas para escribir un programa en C o C++
Escribir el cdigo fuente
Compilar
Ejecucin
Los errores clsicos
Error de compilacin
Error de enlazado
Programacin modular: multi-definicin, bucles de inclusin
Funcion declaradapero no encontrada
Ir ms lejos con la compilacin: makefile

Introduccin
De manera general, el objetivo de un compilador es el de convertir un archivo texto (conteniendo
cdigo fuente) en un archivo binario (por ejemplo un ejecutable). Una vez creado el ejecutable,
ste es ejecutado como cualquier programa. Este programa puede ejecutarse sin que se
disponga del cdigo fuente. Un lenguaje compilado (como C o C++) es diferente a un lenguaje
interpretado (por ejemplo un script de shell) o a un seudo-interpretado (por ejemplo python). En
C, la compilacin transformar el cdigo C de un programa en cdigo nativo, es decir una serie
de instrucciones binarias que pueden ser directamente comprendidas por el procesador.

Instalacin de un compilador C y C++


Bajo Linux
En general se utiliza gcc y g++. Para instalarlo se utiliza su gestor de paquetes habitual. Por
ejemplo bajo Debian (o cualquier distribucin basada en debian) basta con escribir como root o
con un sudo:
aptitude update
aptitude safe-upgrade
aptitude install gcc g++
Del mismo modo podemos instalar un entrono de desarrollo como por ejemplo kdevelop (bajo
KDE) o anjuta (bajo gnome).

Bajo Windows
Podemos utilizar dev-cpp o code::blocks: dos entornos de desarrollo libres que se basan en gcc
y g++: http://www.bloodshed.net/devcpp.html http://www.codeblocks.org/ Articulo relacionado:
http://es.kioskea.net/faq/sujet 2817 compilar un programa en c bajo linux

Las fases de compilacin


La primera fase consiste en escribir el cdigo fuente en lenguaje C o C++ (archivos con
extensin .c y .h en C y .cpp y .hpp en C++). Luego se efecta la compilacin, por ejemplo con
gcc (en C) o g++ (en C++). La compilacin se desarrolla en tres grandes fases.

1) El preprocesado
El compilador comienza por aplicar cada instruccin pasada al preprocesador (todas las lneas
que comienzan con #, entre estas las #define). Estas instrucciones son en realidad muy simples
ya que nicamente copian o eliminan secciones de cdigo sin compilarlas. Es en esta fase que
las #define que se encuentran en un archivo fuente (.c o .cpp) o en un header (.h o .hpp) son
reemplazadas por cdigo C/C+. Al final de esta etapa, no habrn instrucciones comenzando por
#.

2) La compilacin
Luego, el compilador compila cada archivo fuente (.c y .cpp), es decir crea un archivo binario (.o)
para cada archivo fuente, excepto para el archivo conteniendo la funcin main. Esta fase
constituye la compilacin propiamente dicha. Estas dos primeras etapas son realizadas por cc
cuando se utiliza gcc/g++.

3) El enlazado
Finalmente, el compilador une cada archivo .o con los archivos binarios de las libreras que son

utilizadas (archivos .a y .so bajo Linux, archivos .dll bajo Windows). Especialmente, verifica que
cada funcin llamada en el programa no est solamente declarada (esto es hecho durante la
compilacin) sino tambin implementada. Tambin verifica que una funcin no est
implementada en varios archivos .o. Esta fase constituye la fase final para obtener un ejecutable
(.exe bajo Windows, generalmente sin extensin bajo Linux). Esta fase es realizada por ld
cuando se utiliza gcc/g++.

Warning y errores
Evidentemente, en un entorno de desarrollo, basta con hacer clic sobre build y estas tres fases
se llevan a cabo automticamente. No obstante, es importante conocerlas para interpretar
correctamente los mensajes de error o warning de un compilador. Un warning significa que el
cdigo es ambiguo y que puede ser interpretado de manera diferente de un compilador a otro,
pero el ejecutable puede ser creado. En cambio, un error significa que el cdigo no ha podido
ser compilado completamente y que el ejecutable no ha podido ser creado.

Las grandes etapas para escribir un programa en C o C++


Escribir el cdigo fuente
Un simple bloc de notas puede ser suficiente, por ejemplo podemos escribir en el fichero plop.c:
#include <stdio.h>
int main(){
printf("plop !\n");
return 0;
}

Compilar
Bajo Linux llamamos directamente a gcc (-W y Wall permiten mostrar ms mensajes para
verificar si el cdigo es limpio, -o plop.exe indica que el ejecutable que ser creado debe
llamarse plop.exe):
gcc -W -Wall -o plop.exe plop.c
Implcitamente el compilador hace las tres etapas descritas anteriormente. 1) El preprocesado
/* Todo lo que es definido por <stdio.h>, incluyendo printf() */
int main(){
printf("plop !\n");
return 0;
}

2) Compilacin (encuentra sin problemas printf ya que ste es declarado en <stdio.h>) 3)


Enlazado (encuentra sin problemas printf en el binario de la lib c). Tambin lo podemos verificar
bajo Linux con ldd:
ldd plop.exe
Lo que da:
linux-gate.so.1 => (0xb7f2b000)
libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7dbb000)
/lib/ld-linux.so.2 (0xb7f2c000)
En la segunda lnea podemos ver que utiliza la lib c. luego crea plop.exe. Por otra parte
comprobamos que no hay error ni warning.

Ejecucin
Tan solo queda ejecutarlo:
./plop.exe
Lo que da como esperado:
plop!
Si algn error se produce aqu (error de segmentacin, falta de memoria, etc.), por lo general
habr que recurrir a un depurador (por ejemplo gdb o ddd), revisar el cdigo fuente, etc. En todos
los casos, no ser un problema de compilacin. Articulo relacionado: error de segmentacin
Atencin: Bajo Windows existen dos mtodos para ejecutar un programa: Mtodo 1: podemos
ejecutar un programa a travs de los comando ms-dos (haciendo clic en Inicio / Ejecutar y
escribir cmd). Con el comando cd nos colocamos en el directorio y ejecutamos el programa. En
este caso todo ira bien. Mtodo 2: Si ejecutamos el programa desde el explorador, no podremos
ver el programa a menos que pongamos una pausa justo antes del final del programa.
#include <stdio.h>
int main(){
printf("plop !\n");
getchar(); /* el programa se detiene a menos que presionemos una tecla */
return 0;
}

Los errores clsicos


Error de compilacin

Supongamos que olvidamos incluir en nuestro cdigo fuente el archivo <stdio.h> (en el que est
declarada la funcin printf), o un ";", tendremos entonces un mensaje de error de compilacin. Es
el error ms tpico.

Error de enlazado
Estos errores son ms sutiles, ya que no tienen que ver con la sintaxis, sino con la manera en
que esta estructurado y compilado el programa. Son fciles de reconocer cuando se utiliza gcc o
g++ ya que los mensajes de error correspondientes mencionan a ld (el linker). Primer ejemplo:
multidefinicin Un error de enlazado puede ocurrir cuando se escribe el cdigo de un programa
utilizando varios ficheros. A continuacin veremos este tipo de error: Supongamos que nuestro
programa es escrito utilizando 3 archivos: a.h, a.c, y main.c. El encabezado a.h esta incluido en
los dos archivos fuente main.c y a.c. el archivo main.c contiene la funcin main(). 1) Si
compilamos nicamente a.c, el archivo que no contiene la funcin main, es necesario indicarle
al compilador (opcin c en gcc) sino no sabr cmo crear un ejecutable, ya que no hay punto
de partida. Es por ello que el archivo conteniendo la funcin main (sin opcin c) y los otros
archivos fuente se compilan de manera diferente. En este caso:
gcc -W -Wall -c a.c
gcc -W -Wall -o plop.exe main.c a.o
Las opciones W y Wall permiten mostrar ms mensajes de warning. -El primer comando
construye a.o a partir de a.c. -El segundo genera el binario asociado a main.c, lo une con a.o, y
produce as un ejecutable (plop.exe) Podemos observar que si el programa contiene un error en
a.c, el compilador producir un error al momento de compilar a.c. esto provocar errores en
cascada en los otros archivos. Por ello cuando un programa no compila, se comienza por lo
primeros mensajes de error, los solucionamos, lo volvemos a compilar, etchasta que todos los
errores sean solucionados. 2) Recordemos que normalmente, declaramos la funcin en el
encabezado (por ejemplo a.h):
void plop();
y que lo implementamos en el archivo fuente (por ejemplo a.c):
#include "a.h"
#include <stdio.h>
void plop(){
printf("plop !\n");
}
Ahora supongamos que implementamos la funcin plop() en a.h (es decir que la funcin no est
declarada en a.h). En otras palabras, el archivo a.h contiene
#include <stdio.h>
void plop(){
printf("plop !\n");
}

y el fichero a.c contiene por ejemplo:


#include "a.h"
void f(){
plop();
}
El fichero a.h es incluido por main.c y a.c. De este modo el contenido de a.h es copiado en a.c y
en main.c. As, cada uno de estos dos archivos fuentes dispondrn de una implementacin de la
funcin plop(), pero el compilador no sabr cual tomar y generar un error de multi definicin al
momento del enlazado:
(mando@aldur) (~) $ gcc -W -Wall main.c a.o
a.o: In function `plop':
a.c:(.text+0x0): multiple definition of `plop'
/tmp/ccmRKAvQ.o:main.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status
Esto muestra por que en general la implementacin de una funcin se debe hacer en un archivo
fuente (.c o .cpp) y no en un encabezado (.h y .hpp). Solo hay dos excepciones a esta regla en
C+: las funciones inline y las funciones template (o los mtodos de una clase template). Para
mayor informacin ver estos dos artculos: Link1 http://es.kioskea.net/faq/sujet 2823 la funcion
inline en c

Programacin modular: multi-definicin, bucles de inclusin


Supongamos que tenemos 3 archivos: main.c y los archivos a.h y b.h. supongamos que a.h y b.h
se incluyen mutuamente. Estos dos archivos se incluyen mutuamente indefinidamente! Es aqu
que el preprocesador viene a nuestra ayuda, ya que vamos a poder evitar que los #include no se
hagan indefinidamente poniendo en a.h:
#ifndef A_H
#define A_H
#include "b.h"
/* El contenido del encabezado a.h */
#endif
Y en b.h:
#ifndef B_H
#define B_H
#include "a.h"
/* El contenido del header b.h */
#endif
Concretamente, qu es lo que pasar? El compilador comenzar con un archivo (por ejemplo
a.h). Como en ese momento A_H no est definido, ste avanza, luego define a.h y continua

leyendo el encabezado a.h, incluyendo b.h. en ese momento B_H no est definido, por lo tanto
de la misma manera entramos en el encabezado b.h y activamos A_H. Ahora leemos el
contenido de b.h que quiere incluir a.h. Entramos nuevamente en a.h pero esta vez, A_H est
definido por lo que ignoramos el contenido del encabezado. Terminamos de leer el encabezado
b.h, lo que resuelve el #include "b.h" que estbamos tratando en a.h. Luego terminamos el
encabezado b.h. Este caso puede parecer extrao, pero hay que tener en cuenta que cuando un
encabezado est incluido varias veces, pueden aparecer multidefiniciones (en particular si son
declaradas estructuras en el encabezado). Por ello es que ponemos sistemticamente este
mecanismo de cerrojo en todos los encabezado.

Funcion declaradapero no encontrada


Si una funcin es declarada, utilizada, pero no implementada, tambin se producir un error de
enlazado. Esto ocurre tpicamente en dos casos: -se ha declarado una funcin pero aun no se la
ha implementado -se desea utilizar una funcin de una librera, se han incluido los encabezados
correspondientes, pero se ha olvidado pasar como parmetro del compilador dichas libreras.

Ir ms lejos con la compilacin: makefile


Cuando no se dispone de entorno de desarrollo, a fin de evitar teclear manualmente un gcc por
fichero fuente, hacemos un fichero makefile que se ocupa de describir cmo construir el
ejecutable. En la practica, es buena idea escribir un archivo adems de nuestros ficheros C/C+
para que el cdigo sea fcil de compilar. Si este fichero es correctamente escrito, tan solo hay
que ejecutar makefile para construir ntegramente el programa. Bajo Linux escribimos
simplemente el comando:
make
PD: El artculo original fue escrito por Jeff, contribuidor de CommentCaMarche
Este documento intitulado La compilacin y los mdulos en C y C++ de Kioskea (es.kioskea.net) esta puesto a
diposicin bajo la licencia Creative Commons. Puede copiar, modificar bajo las condiciones puestas por la licencia,
siempre que esta nota sea visible.