Programación en C/El proceso de compilación

De Wikilibros, la colección de libros de texto de contenido libre.

Normalmente, a la hora de programar no creamos un único archivo C .c, sino varios de ellos conteniendo diferentes funciones del programa. Esto nos proporciona varias ventajas: una mejor organización del código, una mejor modularidad y, sobre todo, más facilidad (y velocidad) a la hora de compilar.

El proceso de compilación que hemos tratado hasta ahora se divide en realidad en dos etapas, que el compilador nos esconde en una: compilación propiamente dicha y enlazado. En la primera etapa, la de compilación, nuestro código en C se transforma en código objeto, es decir, código máquina (instrucciones que el ordenador puede ejecutar) en ficheros .o, mientras que en la segunda etapa (enlazado) estos ficheros objeto son unidos entre sí para formar el fichero ejecutable (normalmente sin extensión en el mundo Unix, o con extensión .com o .exe en el mundo MS-DOS/Windows).

Archivo:Compilar.png

De esta manera, si no hemos modificado el fichero .c que se compila a un determinado fichero .o podemos ahorrarnos esa parte de la compilación cuando hagamos un cambio en otra parte del programa.

Además tenemos los archivos de cabecera .h, que utilizamos para definir parámetros que se utilizan en el código (caso del fuente1.h del ejemplo) o para definir todas las funciones que vamos a utilizar en todos los archivos .c (caso del cabeceras.h), ya que si recordamos, las funciones (como todo en C) deben definirse antes de usarse, y es posible que estén en otro fichero, lo cual nunca es considerado por el compilador como "antes". Hay que tener en cuenta que el fichero en el que se encuentra la función main() llama, necesariamente, a todos los demás ficheros .c, directa o indirectamente, ya que de lo contrario lo que tendríamos en esos ficheros sería perfectamente inútil.

Si típicamente compilaríamos el ejemplo con

$ gcc -o programa fuente1.c fuente2.c fuente3.c

para realizar esta compilación por pasos, éstos serían:

$ gcc -c fuente1.c                               #crea fuente1.o
$ gcc -c fuente2.c                               #crea fuente2.o
$ gcc -c fuente3.c                               #crea fuente3.o
$ gcc -o programa fuente1.o fuente2.o fuente3.o  #crea programa

ya que la opción -c del compilador lo que le dice es que detenga el proceso antes de enlazar, creando los ficheros .o necesarios.


=Los pasos del proceso= I&D


Lo primero que le ocurre a un fichero .c de código C es el preprocesado. En este paso se sustituyen todas las macros y se eliminan los comentarios. El resultado, si lo vemos independientemente, es un fichero de código C preprocesado, o .i.

El segundo paso es la compilación propiamente dicha, en el que el código C preprocesado se convierte en código ensamblador, que si lo vemos independientemente es un fichero .s.

El tercer paso es el ensamblado del código ensamblador, lo que lo convierte en código máquina. Un fichero de código máquina también es llamado fichero objeto, y su extensión típica es .o.

Dado que el camino anterior normalmente convierte un fichero en un fichero, se suele hacer en un sólo paso, de código C a fichero objeto.

El último paso es el enlazado de los ficheros objeto en el ejecutable final.

Ejemplos[editar]

Tomemos el código C más simple posible:

int main(void) /*Ejemplo*/
{
return(0);
}

Al preprocesarlo tendremos:

# 1 "prueba.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "prueba.c"
int main(void)
{
return(0);
}

Vemos que el comentario ha desaparecido. En su lugar aparecen comentarios específicos del preprocesador. Al compilarlo tenemos:

        .file   "prueba.c"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        addl    $15, %eax
        addl    $15, %eax
        shrl    $4, %eax
        sall    $4, %eax
        subl    %eax, %esp
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)"
        .section        .note.GNU-stack,"",@progbits

Un precioso código ensamblador que enseguida convertimos en un ilegible código máquina:

ELFØ4(  UåäðžÀÀÁèÁà)ÄžÉÃGCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)
.symtab.strtab.shstrtab.text.data.bss.comment.note.GNU-stack4#!XX,X95E@Àñÿ
#prueba.cmain

Ese código máquina es el fichero .o que normalmente obtenemos directamente del código C. Finalmente, ese código objeto es enlazado con las librerías necesarias para formar un ejecutable:

ìÀ£žÿÒ¡žÒuëÆŒÉÃöUå¡ÌÀtžÀtäðPTRhhQVhhè³ÿÿÿôUåSQè[ßüÿÿÿÒtè ÿÿÿX[ÉÃUå=Œt+ën_used__libc_start_mainGLIBC_2.0$ii
  hÌÿÐÄÉÃUåäðžÀÀÁèÁà)ÄžÉÃUåWVSì
                                             è[Ãþè²þÿÿ ÿÿÿ ÿÿÿ)ÐÁøEðuÄ
 [^_]ÃŽ&1ÿÖ¶¿ÿGÆ;}ðrõÄ
   [^_]öŒ'UåWVSì
                                                                                                                           

[à ÿÿÿ ÿÿÿ)ÐÁøEðHøÿt41ÿ¶¿ÿGî9}ðuõèDÄ
                                                                                                                                   [^_]ÃUåSR¡Œøÿt»Œ¶¿ÿÐCüëøÿuóX[]ÃUåSPè[ÃþèVþÿÿX[ÉÃÿÿÿÿÿÿÿÿ$
HÀp                                       \
Y
 þÿÿo$ÿÿÿoðÿÿoÐÈGCC: (GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU) 4.0.3 20051023 (prerelease) 
(Debian 4.0.2-3)GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)GCC: (GNU) 4.0.2 (Debian 4.0.2-2)GCC: 
(GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)GCC: (GNU) 4.0.2 (Debian 4.0.2-2)°",\
                                                                                                                  Ô$$̪q!y_IO_stdin_used°Ò../sysdeps/i386/elf/start.S/
space/debian/glibc/build-area/glibc-2.3.5/
build-tree/glibc-2.3.5/csuGNU AS 2.16.1XÔÔ¬F}xgMint\}nŽO­V|/space/debian/glibc/build-area/glibc-2.3.5/
build-tree/i386-libc/csu/crti.S/space/debian/glibc/build-area/glibc-2.3.5/build-tree/glibc-2.3.5/
csuGNU AS 2.16.1­f(/space/debian/glibc/build-area/glibc-2.3.5/
build-tree/i386-libc/csu/crtn.S/space/debian/glibc/build-area/
glibc-2.3.5/build-tree/glibc-2.3.5/csuGNU AS 2.16.1%
                                                                                                                                                                   $

                                                                                                                                                                >
                                                                                                                                                            $

                                                                                                                                                               >
                                                                                                                                                                4:
                                                                                                                                                                  ;
                                                                                                                                                                I?
                                                                                                                                                                                          
T/û
../sysdeps/i386/elfstart.S°À01:"VWYX û
init.cš^û
/space/debian/glibc/build-area/glibc-2.3.5/build-tree/i386-libc/csucrti.S3,W\#,:Ô
,Wdd,,W^û
/space/debian/glibc/build-area/glibc-2.3.5/build-tree/i386-libc/csucrtn.Sªq    /space/debian/glibc
/build-area/glibc-2.3.5/build-tree/glibc-2.3.5/csuinit.cshort intlong long intunsigned charlong 
long unsigned intshort unsigned int_IO_stdin_usedGNU C 4.0.2 (Debian 4.0.2-2)
.symtab.strtab.shstrtab.interp.note.ABI-tag.hash.dynsym.dynstr.gnu.version.
gnu.version_r.rel.dyn.rel.plt.init.text.fini.rodata.eh_
frame.ctors.dtors.jcr.dynamic.got.got.plt.data.bss.comment.
debug_aranges.debug_pubnames.debug_
info.debug_abbrev.debug_line.debug_str#(( 1HH(7
                                                                                                                                 ppP?ÀÀYGÿÿÿo
Tþÿÿo$$ c       lD      LL
       u\\ptt0{°°ä°žžŒ ħÌ̬ÐеºÃ°°
                        ŒŒÎŒ7×øæp%ö}
v
²0:
'|`!5   Üî(HpÀ$L       \
t
 °
°žŒÄÌаŒ          ŒÄ-Ì:                f@
         rÀȞ̊`
          ŒÐÅŒñÿÖŒñÿéŒñÿúŒñÿ#°*Ž7X
                                                          G\
M°
  Tc
     dŒñÿph#
£ŒñÿªÀñÿ¯ŽŸ°Ë ß call_gmon_start__CTOR_LIST____DTOR_LIST____JCR_LIST__
completed.4463p.4462__do_global_dtors_auxframe_dummy__CTOR_
END____DTOR_END____FRAME_END____JCR_END____do_global_ctors_aux_DYNAMIC__
fini_array_end__fini_array_start__init_array_
end_GLOBAL_OFFSET_TABLE___init_array_start_fp_hw__dso_handle__libc_csu_fini_init_start__libc_csu_init__bss_startmain
__libc_start_main@@GLIBC_2.0data_start_fini_edata_end_IO_stdin_used__data_start_Jv_RegisterClasses__gmon_start__