
















Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity
Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium
Prepara tus exámenes
Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity
Prepara tus exámenes con los documentos que comparten otros estudiantes como tú en Docsity
Encuentra los documentos específicos para los exámenes de tu universidad
Estudia con lecciones y exámenes resueltos basados en los programas académicos de las mejores universidades
Responde a preguntas de exámenes reales y pon a prueba tu preparación
Consigue puntos base para descargar
Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium
Comunidad
Pide ayuda a la comunidad y resuelve tus dudas de estudio
Ebooks gratuitos
Descarga nuestras guías gratuitas sobre técnicas de estudio, métodos para controlar la ansiedad y consejos para la tesis preparadas por los tutores de Docsity
Asignatura: Fundamentos del Software, Profesor: Garvi, Eladio, Carrera: Ingeniería Informática, Universidad: UGR
Tipo: Ejercicios
1 / 24
Esta página no es visible en la vista previa
¡No te pierdas las partes importantes!

















Prácticas de Fundamentos del Software Módulo II. Compilación y depuración de programas
7 - oct- 2014
Lenguajes y Sistemas Informáticos Universidad de Granada
Lenguajes y Sistemas Informáticos Universidad de Granada
Lenguajes y Sistemas Informáticos Práctica 8. Compilación de programas
Práctica 8: Compilación de programas
Conocer cómo la utilidad gcc/g++ realiza las distintas etapas del proceso de generación de un archivo
ejecutable a partir de distintos archivos de código fuente.
Conocer las dependencias que se producen entre los distintos archivos implicados en el proceso de
generación de un archivo ejecutable.
Saber construir un archivo makefile sencillo que permita mantener las dependencias entre los distintos
módulos de un pequeño proyecto software.
Además, se verán las siguientes órdenes:
Tabla 8. 1. Órdenes de la sesión.
2 Introducción a la compilación de programas con gcc/g++
GCC es un compilador integrado del proyecto GNU para los lenguajes C, C++, Objective C y Fortran. A partir de un
archivo que contiene un programa escrito en cualquiera de estos lenguajes puede generar un programa ejecutable
binario en el lenguaje de la máquina donde queremos que se ejecute el programa. En la figura 1 podemos ver el
proceso normal de compilación.
El preprocesador (del inglés, preprocessor) acepta como
entrada un archivo que contiene código fuente ( archivo.c o
archivo.cpp ) y se encarga de eliminar los comentarios e
interpretar las directivas de preprocesamiento. Estas directivas
siempre comienzan por el símbolo # (sí, el mismo que precede
a los comentarios en Bash). La más interesante por ahora de
cara a nuestra práctica es **#include ** o
#include ”archivoinc.h” que sustituye la línea por el
contenido del archivo archivoinc o archivoinc.h.
El compilador (del inglés, compiler) analiza el código fuente
preprocesado y lo traduce a un código objeto que se almacena
en un archivo archivoinc.o , conocido como módulo
objeto. En el proceso de compilación se realiza la traducción
del código fuente a código objeto pero no se resuelven las
posibles referencias a elementos externos al archivo. Las
referencias externas se refieren a variables y funciones que,
aunque se utilizan en el archivo, y por tanto deben estar
declaradas, no se encuentran definidas en éste, sino en otro
archivo distinto.
El enlazador (del inglés, linker) se encarga de resolver las
referencias externas y generar un archivo ejecutable. Estas Figura 8.1. Proceso normal de compilación en C/C++.
Lenguajes y Sistemas Informáticos Práctica 8. Compilación de programas
módulos objeto podemos generar una biblioteca utilizando la orden ar. Puede comprobar lo que significan las
diferentes opciones utilizadas mediante el manual en línea.
En este caso, los siguientes archivos se encuentran en el subdirectorio dirprograma2.
$ g++ - c sin.cpp
$ g++ - c cos.cpp
$ g++ - c tan.cpp
$ ar - rvs libmates.a sin.o cos.o tan.o
Para comprobar el uso que podemos hacer de nuestra biblioteca vamos a generar un programa ejecutable,
programa2, a partir de los siguientes módulos de código objeto: main2.o, factorial.o, hello.o que fueron
compilados cada uno mediante g++ - c.
$ g++ - o programa 2 main 2 .o factorial.o hello.o
main 2 .o: In function 'main':
main 2 .cpp:(.text+ 0 x 58 ): undefined reference to 'print_sin(float)'
main2.cpp:(.text+0x65): undefined reference to 'print_cos(float)'
main 2 .cpp:(.text+ 0 x 72 ): undefined reference to 'print_tan(float)'
collect 2 : ld returned 1 exit status
Ejercicio 8. 2. Explique por qué el enlazador no ha podido generar el programa archivo ejecutable programa2 del
ejemplo anterior y, sin embargo, ¿por qué sí hemos podido generar el módulo main2.o?
Para generar el programa ejecutable hay que especificar explícitamente la(s) biblioteca(s) que se utilizan (vamos,
las definiciones de las funciones).
$ g++ - L./ - o programa2 main2.o factorial.o hello.o - lmates
La opción - L permite especificar directorios en donde g++ puede buscar las bibliotecas necesarias. Por omisión
g++ las busca en los directorios /lib y /usr/lib. Además, en el caso de – lmates, esa opción – l busca la
biblioteca cuya raíz es mates, con prefijo lib y sufijo .a, es decir, busca la biblioteca libmates.a.
Pruebe lo siguiente.
$ mkdir includes
$ mv *.h includes
$ rm *.o programa 2
$ g++ - L./ - o programa 2 main 2 .cpp factorial.cpp hello.cpp - lmates
main2.cpp:2:23: error: functions.h: No such file or directory
main 2 .cpp:3:19: error: mates.h: No such file or directory
main 2 .cpp: In function ‘int main()’:
main 2 .cpp: 8 : error: ‘print_hello’ was not declared in this scope
main 2 .cpp: 10 : error: ‘factorial’ was not declared in this scope
main 2 .cpp: 11 : error: ‘print_sin’ was not declared in this scope
main 2 .cpp: 12 : error: ‘print_cos’ was not declared in this scope
main 2 .cpp: 13 : error: ‘print_tan’ was not declared in this scope
factorial.cpp:1:23: error: functions.h: No such file or directory
hello.cpp:2:23: error: functions.h: No such file or directory
Ejercicio 8. 3. Explique por qué la orden g++ previa ha fallado. Explique los tipos de errores que ha encontrado.
De forma análoga a la opción - L, la opción - I permite especificar directorios en donde g++ puede buscar los
archivos de cabecera (por omisión, se buscan en /usr/include). Esta opción no tiene sentido si todos los
archivos de cabecera se encuentran en el directorio donde estamos ejecutando todas estas órdenes. En ese tipo de
circunstancias, NO es necesario definir tal opción e indicar directorios concretos.
Lenguajes y Sistemas Informáticos Universidad de Granada
$ g++ - I./includes - L./ - o programa 2 main 2 .cpp factorial.cpp hello.cpp - lmates
Como puede comprobar, cuando se trabaja con varios módulos de código fuente se van generando una serie de
dependencias entre ellos. En nuestro caso, el módulo main.cpp depende de los módulos factorial.cpp y
hello.cpp. Además, el módulo main2.cpp depende de los anteriores y de la biblioteca libmates.a. Si en
algún momento es necesario modificar algún módulo, se hace necesario conocer estas dependencias para generar
un nuevo programa ejecutable. Por ejemplo si suponemos que el módulo hello.cpp cambia la línea: cout <<
"Hello World!"; por cout << "Hello happy World!"; sería necesario generar de nuevo el módulo
objeto correspondiente y generar de nuevo el programa1 y el programa2.
Afortunadamente existe una utilidad make que permite gestionar las dependencias (y otras muchas cosas),
comprobando qué archivos se han modificado desde la última vez que se ejecutó para construir el archivo
ejecutable y, en su caso, vuelve a construirlo haciendo de nuevo sólo lo que sea necesario, es decir, compilando
exclusivamente aquellos archivos que hubieran sido modificados.
La idea es especificar en un documento de texto las dependencias entre los archivos y las acciones que deben
llevarse a cabo si se produce alguna modificación. Por ejemplo, si sólo hemos modificado uno de los archivos
fuente o hemos perdido el archivo objeto correspondiente, la utilidad make solamente volverá a generar ese
archivo objeto y realizará la fase de enlazado para construir el archivo ejecutable.
Por ejemplo, como se ha visto en la parte introductoria de esta sesión, si deseamos compilar y enlazar de forma
manual una serie de archivos fuente como son main.cpp, factorial.cpp y hello.cpp para obtener el
archivo ejecutable ejemplo, habría que ejecutar lo siguiente:
$ g++ - o programa1 main.cpp factorial.cpp hello.cpp
Esta ejecución compila cada archivo fuente y genera sus correspondientes archivos objeto main.o, factorial.o
y hello.o, respectivamente, y a continuación los enlaza para obtener el archivo ejecutable denominado
programa1.
Es posible automatizar el proceso de compilación y enlazado construyendo un archivo de tipo makefile en el que se
especifiquen las acciones a realizar. En las siguientes secciones se describirá cómo hacerlo.
4.1 Ejecución de la utilidad make
La utilidad make admite entre sus opciones la especificación del nombre de un archivo de tipo makefile. Esa opción
es – f y, a continuación, el nombre del archivo. Si el nombre elegido para el archivo es makefileGNU, makefile
o Makefile, en ese caso, no es necesario especificar la opción anterior junto al nombre del achivo, es decir,
bastará con ejecutar la utilidad make sin argumentos.
Por ejemplo, suponiendo que el único archivo makefile se denomina makefileA, se observa el siguiente resultado
tras la ejecución de la orden make.
$ make
make: *** No se especificó ningún objetivo y no se encontró ningún makefile. Alto.
Para que admita el ese archivo makefile, se ha de especificar su nombre mediante la opción – f.
$ make - f makefileA
En cambio, si el archivo se denominase makefile, entonces la ejecución de la orden make no necesitaría tal
opción.
Lenguajes y Sistemas Informáticos Universidad de Granada
main.o: main.cpp
g++ - I./includes - c main.cpp
factorial.o: factorial.cpp
g++ - I./includes - c factorial.cpp
hello.o: hello.cpp
g++ - I./includes - c hello.cpp
Nótese que para el objetivo programa1 existen tres dependencias que son las de la existencia de los archivos
objeto main.o, factorial.o y hello.o. Dichas dependencias son, a su vez, otros objetivos descritos en el
mismo archivo makefile.
Suponiendo que no existen los archivos objeto ni el ejecutable, la ejecución de la utilidad make con este archivo
arrojaría el siguiente resultado:
$ make – f makefileB
g++ - I./includes - c main.cpp
g++ - I./includes - c factorial.cpp
g++ - I./includes - c hello.cpp
g++ - o programa 1 main.o factorial.o hello.o
Si ahora borramos uno de los archivos objeto, por ejemplo factorial.o, el resultado de ejecutar la utilidad
make sería el siguiente:
$ make – f makefileB
g++ - I./includes - c factorial.cpp
g++ - o programa 1 main.o factorial.o hello.o
En esta ejecución podemos observar que sólo será necesaria la obtención del archivo objeto factorial.o y el
posterior enlazado dado que los otros dos archivos objeto ya existían y no han sido modificados sus archivos
fuente.
Basándonos en este mismo ejemplo, el archivo functions.h está incluido en los archivos fuente main.cpp,
factorial.cpp y hello.cpp. ¿Qué ocurriría si modificásemos el archivo functions.h? En ese caso, no se
recompilaría absolutamente nada porque en el archivo makefileB no existe ninguna dependencia en la que
intervenga. Para solucionarlo, sólo hemos de incluir este archivo de cabecera en las dependencias de aquellos
objetivos donde su archivo fuente lo incluya. Si el archivo functions.h estuviera ubicado en el mismo directorio
que el resto de archivos fuente, el nuevo archivo makefile, que denominaremos makefileC, quedaría de la
siguiente forma:
programa 1 : main.o factorial.o hello.o
g++ - o programa1 main.o factorial.o hello.o
main.o: main.cpp functions.h
g++ - c main.cpp
factorial.o: factorial.cpp functions.h
g++ - c factorial.cpp
hello.o: hello.cpp functions.h
g++ - c hello.cpp
Suponiendo que se modifica el archivo functions.h, se deberían recompilar los archivos main.cpp,
factorial.cpp y hello.cpp. Pruébelo y compruebe que se realiza tal y como se ha explicado.
Lenguajes y Sistemas Informáticos Práctica 8. Compilación de programas
Es importante reseñar que este ejemplo, descrito según el makefile anterior, funciona perfectamente pues
detectaría posibles cambios realizados al archivo functions.h al encontrarse éste en el mismo directorio que el
resto de los archivos fuente.
Si el archivo functions.h estuviese en otro directorio, habría que indicar la ruta para acceder a él tanto en la
dependencia como en la orden donde interviene. En el siguiente ejemplo, suponiendo que se encuentra en un
subdirectorio denominado includes, el archivo makefileC, ahora denominado makefileD, pasaría a ser el
siguiente:
programa 1 : main.o factorial.o hello.o
g++ - o programa1 main.o factorial.o hello.o
main.o: main.cpp ./includes/functions.h
g++ - I./includes - c main.cpp
factorial.o: factorial.cpp ./includes/functions.h
g++ - I./includes - c factorial.cpp
hello.o: hello.cpp ./includes/functions.h
g++ - I./includes - c hello.cpp
Es posible construir una regla que no tenga órdenes asociadas. En este caso, cuando make trate de construir el
objetivo de esta regla simplemente comprobará que los archivos de la lista de dependencias están actualizados. Si
es necesario construir alguno, pasa a ejecutar la regla que tiene este archivo como objetivo. Este tipo de reglas se
conocen como objetivos simbólicos o destinos simbólicos. Si no se especifica ninguna regla en la ejecución de la
utilidad make, se procesan las reglas desde la primera que se encuentre en adelante. Para aprender más sobre la
ejecución de la orden make puede consultar el manual en línea.
Además, se puede invocar una regla sin dependencias a la hora de ejecutar la utilidad make. A ese tipo de reglas
se las denomina reglas virtuales. Por ejemplo, podemos crear una regla virtual cuyo objetivo denominaremos
clean y cuya funcionalidad sea la de eliminar los archivos objeto generados. Recuerde que, al no ser un objetivo
que intervenga en la consecución del objetivo principal, generalmente se sitúan al final de las demás reglas.
A continuación se puede ver cómo quedaría dentro de un archivo makefile cualquiera:
clean:
rm *.o
Para ejecutar esta última regla es necesario especificar el nombre del objetivo a la hora de invocar a la utilidad
make, tal y como se muestra a continuación:
$ make - f makefile clean
Busque en internet el problema que se puede dar en las reglas virtuales cuando se crea un archivo en el directorio
con el mismo nombre de la regla (por ejemplo, clean). ¿Cómo se soluciona?
En general, la utilidad make, permite la ejecución de una regla cualquiera invocando el nombre del objetivo de la
regla como argumento del make.
En un archivo de tipo makefile, también se pueden añadir comentarios anteponiendo el símbolo # en su primera
columna y se pueden extender a lo largo de toda una línea de texto. Si deseamos varias líneas de comentario cada
una de ellas deberá comenzar por #.
Lenguajes y Sistemas Informáticos Práctica 8. Compilación de programas
CC=g++
INCLUDE_DIR= ./includes
programa 2 : main 2 .o factorial.o hello.o libmates.a
$(CC) - L$(LIB_DIR) - o programa 2 main 2 .o factorial.o hello.o - lmates
main 2 .o: main 2 .cpp
$(CC) - I$(INCLUDE_DIR) - c main2.cpp
factorial.o: factorial.cpp
$(CC) - I$(INCLUDE_DIR) - c factorial.cpp
hello.o: hello.cpp
$(CC) - I$(INCLUDE_DIR) - c hello.cpp
libmates.a: sin.o cos.o tan.o
ar - rvs libmates.a sin.o cos.o tan.o
sin.o: sin.cpp
$(CC) - I$(INCLUDE_DIR) - c sin.cpp
cos.o: cos.cpp
$(CC) - I$(INCLUDE_DIR) - c cos.cpp
tan.o: tan.cpp
$(CC) - I$(INCLUDE_DIR) - c tan.cpp
En concreto, se han definido las siguientes variables: CC , INCLUDE_DIR y LIB_DIR. Estas variables se pueden
usar posteriormente en las declaraciones de las reglas simplemente incluyéndolas entre paréntesis o llaves y
anteponiéndoles el signo $. Además existen variables especiales que actúan cuando make procesa cada regla. A
continuación se muestran algunas de ellas:
Variable Significado
Representa el nombre del objetivo de la regla en la que nos encontramos
Representa la primera dependencia de la regla en la que nos encontramos
Representa las dependencias de la presente regla que hayan sido actualizadas (modificadas)
dentro del objetivo de la regla y separadas por un espacio en blanco
Representa todas las dependencias separadas por un espacio en blanco
Tabla 8.2. Variables especiales de la orden make.
En los siguientes epígrafes se mostrarán ejemplos de uso de estas variables. Para ello vamos a suponer las
siguientes declaraciones en un archivo makefile:
CC = g++
CPPFLAGS = - Wall
SRCS = main.cpp factorial.cpp hello.cpp
OBJS = main.o factorial.o hello.o
HDRS = functions.h
En la variable CPPFLAGS se indican las opciones del compilador. En este caso, la opción – Wall sirve para mostrar
todos los warnings que pudieran aparecer durante el proceso de compilación.
Lenguajes y Sistemas Informáticos Universidad de Granada
4.3.1 Uso de $@
El caso que se muestra a continuación sirve para representar el nombre que se le asociará al programa ejecutable
usando para ello el mismo nombre asignado al objetivo:
programa1: $(OBJS)
$(CC) - o $@ $(OBJS)
Como puede verse, el valor de $@ en la regla se sustituirá por programa1.
4.3.2 Uso de $<
Esta variable se utiliza para representar el archivo aportado como primera dependencia en una regla. De este
modo, por ejemplo, en un proceso de compilación, a la hora de indicar la orden para la obtención del archivo
objeto, en lugar de referenciar el nombre de ese primer archivo, se puede representar con esta variable.
hello.o: hello.cpp
$(CC) - c $(CPPFLAGS) $<
Como puede verse en esta ocasión, el valor de $< en la regla se sustituirá por hello.cpp, es decir, la primera
dependencia (y única en esta ocasión) de la regla.
4.3.3 Uso de $?
Cuando se desea hacer referencia a varias de las dependencias a la hora de actuar en consecuencia con las
órdenes de una regla, es posible usar esta variable para indicarlas y, en concreto, referenciaría aquellas
dependencias que se hubieran actualizado:
print: $(SRCS)
lpr - p $?
Con esta regla, si se ejecutase la utilidad make sobre este archivo makefile junto con el argumento print, la
orden imprimiría aquellos archivos que hubieran sido modificados hasta ese momento. Esta opción es muy útil
cuando se desea disponer de una copia impresa de los archivos fuente. Si en un momento anterior se hubieran
imprimido todos los archivos fuente y, posteriormente, se hubiera modificado sólo uno de ellos, la acción se llevaría
a cabo imprimiendo únicamente ese archivo y no los demás.
4.3.4 Uso de $^
Cuando se desea referenciar a todos los archivos indicados en las dependencias de una regla, lo cual supondría el
ahorro de tener que escribirlos en varios lugares del archivo makefile, se utiliza la variable $^.
programa1: $(OBJS)
$(CC) - o $@ $^
Como puede verse en esta ocasión, el valor de $^ en la regla se sustituirá por los nombres de todas sus
dependencias, es decir, por la secuencia main.o factorial.o hello.o. Además, tal y como se ha mostrado
anteriormente, se usa la variable $@ para indicar el nombre del objetivo como nombre de archivo ejecutable.
4.3.5 Otras opciones y variables
Si la utilidad make se ejecuta con la opción – p nos muestra las variables predefinidas que se pueden usar dentro
de la especificación de un archivo makefile.
Lenguajes y Sistemas Informáticos Universidad de Granada
Ejercicio 8. 10. Con la siguiente especificación de módulos escriba un archivo denominado makefilePolaca que
automatice el proceso de compilación del programa final de acuerdo a la siguiente descripción:
Compilador: gcc o g++
Archivos cabecera: calc.h (ubicado en un subdirectorio denominado cabeceras)
Archivos fuente: main.c stack.c getop.c getch.c
Nombre del programa ejecutable: calculadoraPolaca
Además, debe incluir una regla denominada borrar, sin dependencias, cuya funcionalidad sea la de eliminar los
archivos objeto y el programa ejecutable.
Lenguajes y Sistemas Informáticos Práctica 9. Depuración de programas
Práctica 9 : Depuración de programas
Conocer cómo la utilidad gdb es capaz de seguir la traza de ejecución en ejecutables compilados con código
fuente en un lenguaje admitido por gcc (C++).
Conocer las herramientas básicas de depurado y obtención de información de gdb.
Saber construir guiones para gdb para automatizar la depuración.
Conocer el manejo de marcos (frames) en gdb.
Saber utilizar las órdenes de gdb para modificar la ejecución de un programa y sus datos.
Conocer las órdenes avanzadas de depuración de procesos en gdb.
Además, se verán las siguientes órdenes:
Tabla 9. 1. Órdenes de la sesión.
9.2 Introducción a la depuración de programas con gdb
La utilidad gdb o GNU Debugger es el depurador estándar para el sistema operativo GNU. Es un depurador
portable que se puede utilizar en varias plataformas Unix y funciona para varios lenguajes de programación como
ensamblador, C, C++ o Fortran.
Esta utilidad ofrece la posibilidad de trazar y modificar la ejecución de un programa. El usuario puede controlar y
alterar los valores de las variables internas del programa.
Además, la utilidad no contiene su propia interfaz gráfica de usuario y por defecto se controla mediante una
interfaz de línea de órdenes. Existen diversos front-end que han sido diseñados para gdb, como DDD,
GDBtk/Insight y el "modo GUD" en Emacs.
Por tanto, gdb, permite ver qué pasa dentro de un programa cuando éste se ejecuta, o qué pasó cuando el
programa dio un fallo y abortó.
El esquema normal de funcionamiento es el siguiente:
a poder depurar el programa. Esta opción – g se ha de situar en la fase de compilación, aunque si con una
sola orden se efectúa la compilación y el enlazado, entonces se deberá incluir igualmente en esa orden (ver
ejemplo más abajo).
Tomando los archivos de esta sesión de prácticas, procederemos a generar un archivo ejecutable a partir de
algunos de los archivos fuentes y luego ejecutaremos el depurador añadiendo, como argumento, el archivo
ejecutable obtenido fruto del proceso de compilación.
$ g++ - g main.cpp hello.cpp factorial.cpp - o ejemplo 1
Lenguajes y Sistemas Informáticos Práctica 9. Depuración de programas
En gdb se pueden añadir puntos de ruptura simple que permiten examinar qué hace el programa en un
determinado lugar. Para ello se puede utilizar la orden break. Esta orden puede tomar como parámetro el nombre
de una función, la dirección lógica donde parar, o un número de línea. Para continuar el programa hasta el final o
hasta el próximo punto de ruptura (lo que llegue antes), se puede utilizar la orden continue.
Ejercicio 9. 3. Ponga un punto de ruptura asociado a cada línea del programa fuente mainsesion09a.cpp
donde aparezca el comentario /* break */. Muestre información de todas las variables que se estén usando
cada vez que en la depuración se detenga la ejecución. Muestre la información del contador de programa mediante
$pc y el de la pila con $sp.
Una vez detenidos a causa a un punto de ruptura, podremos avanzar a la siguiente instrucción del programa con la
orden next o con step. Ambas órdenes se verán en la siguiente sesión.
Los puntos de ruptura activos pueden verse con info breakpoints. Podemos eliminar un punto de ruptura con
la orden delete (para mayor detalle, vea la ayuda mediante la orden help delete).
Ejercicio 9. 4. Indique las órdenes necesarias para ver el valor de las variables final1 y final2 del programa
generado en el ejercicio anterior en los puntos de ruptura correspondientes tras un par de iteraciones en el bucle
for. Indique la orden para obtener el código ensamblador de la zona depurada.
Ejercicio 9. 5. Considerando la depuración de los ejercicios anteriores, elimine todos los puntos de ruptura salvo el
primero.
Para no tener que escribir las acciones en la propia interfaz de gdb, podemos hacer uso de los guiones de gdb.
Un guion de este tipo es un archivo de texto con diversas líneas de órdenes. Por ejemplo, el siguiente cuadro
indica un guion denominado guion.gdb con órdenes para la depuración de un programa denominado ejemplo
y, justo debajo, cómo invocar a dicho guion cuando se desee aplicar la depuración establecida en el guion al
programa en cuestión:
break multiplica
run
display x
display y
display final
continue
continue
delete display 1
delete display 2
continue
$ gdb - x guion.gdb ejemplo 1
Ejercicio 9. 6. Realice las acciones del ejercicio 9.3 y las del ejercicio 9.5 en un guion y ejecútelas de nuevo
mediante la opción - x de gdb. ¿Sabría decir qué hace este programa con la variable final2?
Ejercicio 9. 7. Realice la depuración del programa ejecutable obtenido a partir del archivo fuente
ejsesion09a.cpp. Utilizando gdb, trate de averiguar qué sucede y por qué no funciona. Intente arreglar el
programa.
Lenguajes y Sistemas Informáticos Universidad de Granada
9.7 Depuración avanzada de programas con gdb: marcos(frames)
Un programa contiene información acerca de las direcciones donde se van a ejecutar determinadas funciones del
mismo. A esta información se le denomina la pila de llamadas (call stack). Esta clase de pila también se conoce
como una pila de ejecución, pila de control, pila de función, o pila de tiempo de ejecución, y a menudo se describe
en forma abreviada como “la pila”.
La pila de llamadas se divide en secciones contiguas llamadas pila de marcos ( stack frames) o simplemente
marcos ( frames). Cada marco es el conjunto de datos asociados con una llamada a una función. El marco contiene
los argumentos que se le da a la función, sus variables locales, y la dirección en la cual dicha función se ejecuta.
Cuando el programa comienza, la pila contiene solamente un único marco (en C/C++ el marco contiene solamente
la función main). Cada vez que se llama a una función, se crea un nuevo marco. Cada vez que la función devuelve
algo, el marco asignado a dicha función se elimina. El marco de la función que se está ejecutando actualmente se
denomina marco más interno ( innermost frame).
En el programa, el marco se identifica por su dirección. Un marco consta de muchos bytes, cada uno de los cuales
tiene su propia dirección. Normalmente esta dirección se almacena en un registro llamado registro puntero al
marco ( frame pointer register) mientras se ejecuta el siguiente marco.
La utilidad gdb emplea marcos en la depuración de un programa. La instrucción info frame muestra información
acerca del marco actual. Mientras que backtrace full nos muestra la información referente a las variables
locales y el resto de información asociada al marco.
Usando los archivos de esta sesión, compile con opciones de depuración el archivo mainsesion09b.cpp y
genere su programa ejecutable llamado ejemplo09b.
$ g++ - g – o ejemplo09b mainsesion 0 9b.cpp
Usando el archivo ejecutable obtenido tras la compilación, un ejemplo de información del marco de este ejecutable
sería:
$ gdb ejemplo09b
GNU gdb (Ubuntu/Linaro 7.3- 0 ubuntu 2 ) 7.3-2011.
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv 3 +: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i 686 - linux-gnu".
Para las instrucciones de informe de errores, vea:
...
Leyendo símbolos desde /home/usuario/ejemplo09b...hecho.
(gdb) break cuenta
Punto de interrupción 1 at 0x80485aa: file mainsesion09b.cpp, line 13.
(gdb ) run
Starting program: /home/usuario/ejemplo09b
Breakpoint 1, cuenta (y=0) at mainsesion09b.cpp: 13
13 tmp = y + 2;
(gdb) info frame
Stack level 0 , frame at 0 xbffff 2 b 0 :