compilacion
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
| compilacion [2012/09/06 03:27] – [Preproceso] lmateu | compilacion [2014/09/04 13:42] (actual) – [Preproceso] lmateu | ||
|---|---|---|---|
| Línea 1: | Línea 1: | ||
| - | ===== Fases de la Compilación ===== | + | ===== Etapas |
| La compilación de un programa en C pasa por varias etapas desde | La compilación de un programa en C pasa por varias etapas desde | ||
| Línea 49: | Línea 49: | ||
| Recuerde que cada identificador que se usa en C debe haber sido declarado previamente, | Recuerde que cada identificador que se usa en C debe haber sido declarado previamente, | ||
| modo se considera un error. | modo se considera un error. | ||
| - | de un tipo en un archivo, pero de otro en otro archivo. | + | de un tipo en un archivo, pero de otro tipo en otro archivo. |
| este caso porque simplemente no tiene la información para detectar el error. | este caso porque simplemente no tiene la información para detectar el error. | ||
| se denomina compilación **separada**. | se denomina compilación **separada**. | ||
| Línea 77: | Línea 77: | ||
| % gcc -E prog.c | % gcc -E prog.c | ||
| | | ||
| - | La salida | + | La salida |
| int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||
| Línea 148: | Línea 148: | ||
| pendientes, es decir las llamadas a funciones definidas en otros archivos. | pendientes, es decir las llamadas a funciones definidas en otros archivos. | ||
| - | Cuidado, en esta fase no se realiza ninguna verificación de tipos. | + | Por ejemplo supongamos que tenemos 2 archivos a.c y b.c: |
| + | |||
| + | /* Este es a.c */ | ||
| + | int g(int x); | ||
| + | int a= 1; | ||
| + | float b; | ||
| + | |||
| + | int f() { | ||
| + | b= 1.0; | ||
| + | return g(a); | ||
| + | } | ||
| + | |||
| + | En a.c la referencia pendiente es g porque el ensamblador no puede determinar cual es la dirección de | ||
| + | la función g. | ||
| + | |||
| + | /* Este es b.c */ | ||
| + | #include < | ||
| + | extern int a; | ||
| + | float b; | ||
| + | |||
| + | int main() { | ||
| + | int x= f(); | ||
| + | printf(" | ||
| + | return x; | ||
| + | } | ||
| + | |||
| + | int g(int x) { | ||
| + | a= 2; | ||
| + | return x; | ||
| + | } | ||
| + | |||
| + | Aquí las referencias pendientes son a y f. Observe que a fue declarada como ' | ||
| + | al ensablador que no reserve espacio para ella porque se trata de una promesa de que otro archivo | ||
| + | la va a declarar. | ||
| + | ' | ||
| + | |||
| + | La fase de link en Unix la realiza el comando '' | ||
| + | un error histórico, porque su tarea no es la de un //loader// (cargardor). | ||
| + | es la componente del núcleo del sistema operativo que carga un archivo ejecutable en la memoria del | ||
| + | computador para que sea ejecutado. | ||
| + | se conserva quizás por compatibilidad. | ||
| + | |||
| + | % ld a.o b.o ... otros argumentos ... | ||
| + | |||
| + | |||
| + | ¿Pero que pasa con b que aparece declarada en dos archivos? | ||
| + | eliminando una de las declaraciones. | ||
| + | se puede hacer si la variable se inicializa en los 2 archivos (aunque sea el mismo valor). | ||
| + | En ese caso el linker reporta la variable como una definición múltiple. | ||
| + | |||
| + | === Inconsistencia de tipos === | ||
| + | |||
| + | Cuidado, el linker no realiza ninguna verificación de tipos. | ||
| que el archivo a.c contiene: | que el archivo a.c contiene: | ||
| Línea 155: | Línea 207: | ||
| Y el archivo b.c contiene: | Y el archivo b.c contiene: | ||
| + | #include < | ||
| extern float a; | extern float a; | ||
| | | ||
| int main() { | int main() { | ||
| - | printf(" | + | printf(" |
| } | } | ||
| Línea 165: | Línea 218: | ||
| evitar este tipo de errores. | evitar este tipo de errores. | ||
| + | === Static === | ||
| + | Un error típico en grandes proyectos ocurre cuando 2 archivos implementan funciones con el mismo nombre. | ||
| + | El linker no puede determinar cual usar, y reporta la función como una definición múltiple. | ||
| + | Para disminuir este tipo de errores se puede limitar la visibilidad de una funciona o variable global | ||
| + | a solo el archivo en donde declara: | ||
| + | |||
| + | static int f() { | ||
| + | ... | ||
| + | } | ||
| + | |||
| + | De esta forma si otra función f se declara en otro archivo, no habrá colisión de nombres. | ||
| + | Observe que uso de static acá no tiene nada que ver con el atributo static de Java. | ||
| + | |||
| + | ==== Las bibliotecas ==== | ||
| + | |||
| + | Las bibliotecas son archivos con la extensión ' | ||
| + | de manejarlos como una unidad. | ||
| + | |||
| + | /* bib1.c */ | ||
| + | void g(); | ||
| + | | ||
| + | void f() { | ||
| + | g(); | ||
| + | } | ||
| + | |||
| + | /* bib2.c */ | ||
| + | void g() { | ||
| + | } | ||
| + | |||
| + | /* bib3.c */ | ||
| + | void h() { | ||
| + | } | ||
| + | |||
| + | Compilamos ambos archivos con: | ||
| + | |||
| + | % gcc -c bib1.c bib2.c bib3.c | ||
| + | |||
| + | Lo que genera los archivos bib1.o y bib2.o. | ||
| + | |||
| + | % ar r bib.a bib1.o bib2.o bib3.o | ||
| + | |||
| + | Ahora podemos usar f o g desde otro programa: | ||
| + | |||
| + | /* a.c */ | ||
| + | int main() { | ||
| + | f(); | ||
| + | } | ||
| + | |||
| + | Compilamos incluyendo la biblioteca con: | ||
| + | |||
| + | % gcc a.c bib.a | ||
| + | |||
| + | Como a.o incluye una referencia pendiente a f, el linker la busca en las bibliotecas y la encuentra | ||
| + | en bib1.o, por lo tanto agrega bib1.o completo al binario. | ||
| + | referencia pendiente a g y la encuentra en bib2.o al binario. | ||
| + | lado, el archivo bib3.o no se agrega al binario ejecutable. | ||
| + | |||
| + | Uno de los bugs más difíciles de diagnosticar que me ha tocado presenciar ocurrió cuando | ||
| + | un función de biblioteca llamaba a otra función dentro de la misma biblioteca pero que casualmente | ||
| + | se llamaba igual que una variable global de los fuentes. | ||
| + | si no que rellenó la dirección con la de la variable global, que ni siquiera erá código ejecutable. | ||
| + | ¡El programa se caía con un segmentation fault inexplicable! | ||
| + | |||
| + | Esto se puede reproducir cambiando a.c por: | ||
| + | |||
| + | /* a.c */ | ||
| + | float g= 3.14; | ||
| + | int main() { | ||
| + | f(); | ||
| + | } | ||
| + | Ejercicio: Compile y ejecute para observar el segmentation fault. | ||
| + | la ejecución hacia la variable en punto flotante g, como si ahí hubiesen instrucciones. | ||
compilacion.1346902027.txt.gz · Última modificación: por lmateu
