debug en microsoft visual c++ (2010) express editionejecutar el código por pasos. el menú depurar...

15
Debug en Microsoft Visual C++ (2010) Express Edition Los errores de programación pertenecen a tres categorías: errores de compilación, errores en tiempo de ejecución y errores lógicos. Los errores en tiempo de ejecución son aquéllos que aparecen mientras se ejecuta un programa, normalmente cuando se intenta una operación que es imposible de llevar a cabo (ej.: una división por cero). Los errores lógicos son errores que impiden que un programa haga lo que estaba previsto. Sin embargo, el código se compila y ejecuta sin problemas. Para encontrar y corregir ambos tipos de errores se pueden usar las herramientas de depuración, en este caso veremos las que provee el IDE del MSVC++.

Upload: others

Post on 29-Oct-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

Debug en Microsoft Visual C++ (2010) Express Edition

Los errores de programación pertenecen a tres categorías: errores de compilación, errores

en tiempo de ejecución y errores lógicos.

Los errores en tiempo de ejecución son aquéllos que aparecen mientras se ejecuta un

programa, normalmente cuando se intenta una operación que es imposible de llevar a cabo

(ej.: una división por cero). Los errores lógicos son errores que impiden que un programa

haga lo que estaba previsto. Sin embargo, el código se compila y ejecuta sin problemas.

Para encontrar y corregir ambos tipos de errores se pueden usar las herramientas de

depuración, en este caso veremos las que provee el IDE del MSVC++.

Page 2: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

Si aún no instaló ni sabe cómo usar el IDE puede buscar la guía de instalación y utilización

básica del VC++ Express Edition (lo único que utiliza archivos en C++ con extensión cpp y

no en C con extensión .c): http://campus.ort.edu.ar/descargar/articulos/169935/

A modo de ejemplo, se intenta depurar un programa que compiló correctamente (sin errores

de compilación ni warnings) pero, al ejecutarlo se pone en evidencia que no funciona

correctamente. Recuerde al grabar utilizar la extensión .c.

#include <stdio.h> int toPercent (float decimal); void main() { int a, b; float c; int cAsPercent; printf("Ingrese 2 enteros: "); scanf("%d %d",&a,&b); if(a=b) printf("Son iguales!\n"); else if(a>b) printf("El primer entero es mayor!\n"); else printf("El segundo entero es mayor!\n"); printf("Ingrese un decimal para convertir en porcentaje: "); scanf("%f",&c); cAsPercent = toPercent(c); printf("En %% es: %d\n",cAsPercent); } /* ToPercent(): Convierte un float (ej 0.9) a porcentaje (90). */ int toPercent (float decimal) {

int result; result = (int)decimal * 100; return result;

}

Sin embargo: 1 y 3 no son iguales y 0.85 no es 0%. Para encontrar los problemas que posse

el código se usarán las herramientas de debug del VC++.

En lugar de ejecutar el programa (Ctrl+F5), para hacer un debug, en el Menú Depurar elegir

Iniciar Depuración o, F5 o, en la barra de herramientas .

Page 3: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

Breakpoints

Los breakpoints o puntos de interrupción son el alma de la depuración. Permiten pausar el

programa en alguna línea del código y examinar qué está ocurriendo en detalle. En el

programa anterior se colocará un breakpoint en la línea donde se ingresan por teclados los

dos números a y b que, el programa al ejecutarse, considera que son iguales (en este

ejemplo no lo eran). Yendo con el cursor a esa línea y haciendo click con el botón derecho

del mouse elegir del menú que aparece Punto de interrupción/Insertar Punto de Interrupción

o pulsar F9 o en el menú Depurar elegir Alternar puntos de interrupción. Aparecerá, a la

izquierda de la línea, el símbolo de breakpoint en rojo; haciendo click con el botón derecho

del mouse sobre él, aparece un menú donde se puede borrarlo/deshabilitarlo o asignarle

algunas propiedades.

Ahora, al iniciar la depuración (F5), aparecerá muy rápidamente la ventana de salida

(pantalla de comandos con el resultado del printf de la línea anterior) pero, el control de la

ejecución la minimizará y aparecerá una flecha amarilla en la línea donde está el

breakpoint, indicando la siguiente línea a ejecutar! (pero que aún no se ejecutó).

Casi siempre se usan estos puntos de interrupción “planos” y, no se utilizan algunas

propiedades que tienen, las cuales pueden verse abriendo Depurar/Ventanas/Puntos de

interrupción:

Para lograr una flexibilidad aún mayor, el depurador de Visual Studio permite establecer

propiedades que modifican el comportamiento de los puntos de interrupción:

Condición es una expresión que determina si el punto de interrupción tiene efecto o

no. Cuando el depurador llega al punto de interrupción, evalúa la condición. El

punto de interrupción sólo detiene la ejecución si la condición se cumple. Puede

utilizar una condición con un punto de interrupción de ubicación para detener la

ejecución en un lugar determinado únicamente si la condición se cumple. Por

ejemplo, supongamos que va a depurar un programa de un banco, y que no se

Page 4: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

permite que el saldo de una cuenta sea inferior a cero. Puede establecer puntos de

interrupción en lugares específicos del código y asociar a cada uno la condición

balance < 0. Cuando se ejecute el programa, la ejecución se interrumpirá en esos

puntos si el saldo es inferior a cero. Entonces podrá examinar las variables y el

estado del programa en la ubicación del primer punto de interrupción, continuar la

ejecución hasta el segundo, etc.

Haciendo click con el botón derecho sobre el símbolo del breakpoint aparece la caja

de texto y botones para establecer una condición para el punto de interrupción:

El Recuento de visitas o Número de llamadas, permite determinar cuántas veces se

pasa por un punto de interrupción antes de detener la ejecución. De forma

predeterminada, el depurador detiene la ejecución cada vez que se llega al punto de

interrupción. Puede establecer un recuento de visitas para indicar al depurador que

detenga la ejecución después de llegar 2 veces al punto de interrupción, o 10, 512 o

la cantidad de veces que desee. Los recuentos de visitas resultan útiles, porque

algunos errores no aparecen la primera vez que el programa ejecuta un bucle, llama

a una función, o tiene acceso a una variable. En ocasiones, puede que el error no se

manifieste hasta la iteración número 100 o 1000. Para depurar estos problemas,

puede establecer un punto de interrupción con un recuento de visitas de 100 o 1000.

Haciendo click con el botón derecho del mouse sobre el breakpoint y desplegando la

lista que aparece puede utilizar esta propiedad:

Page 5: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

La ventana de puntos de interrupción permite también, por ejemplo, buscar por el

nombre una función (utilidad en caso de código muy extenso) para insertar un

breakpoint. En Nuevo de esa ventana/Interrumpir en función…:

Ventanas de inspección

En la ventana Inspección puede agregar las variables cuyo valor desee inspeccionar, por lo

tanto comprobar si alguna variable cambia de valor en forma inesperada. Sin embargo,

puede agregar algo más que variables; puede añadir cualquier expresión válida reconocida

por el depurador. Algunas ediciones de Visual Studio tienen varias ventanas Inspección,

que vienen numeradas de Inspección1 a Inspección4. Para abrirlas en el menú

Depurar/Ventanas/Inspección 1.

Si hay variables ingresadas y desea borrarlas, haga click sobre la línea donde está y apriete

la tecla Supr (o haga click con botón derecho y Eliminar inspección).

En este ejemplo, las 2 variables involucradas son a y b. Para añadir a se hace click sobre la

caja de texto de la ventana, se tipea el nombre de la variable y se pulsa Enter para añadirla a

Page 6: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

la lista de inspección. De la misma forma, en la siguiente línea se añade b para poder

observar sus valores:

Los dos números, por coincidencia, contienen el mismo valor, totalmente inútiles

(“basura”, normalmente números negativos extremadamente grandes) puesto que, aún no

han sido inicializados ya que, aún no se ejecutó la línea donde se realiza el ingreso de sus

valores por teclado.

Para poder determinar qué es lo que está sucediendo con estas variables hay que ejecutar

algo más de código.

Ejecución por pasos

Una vez pausada la ejecución (en este caso por el breakpoint) se tiene la posibilidad de

ejecutar el código por pasos.

El menú Depurar contiene tres comandos para avanzar paso a paso por el código:

Paso a paso por instrucciones (F11)

Paso a paso por procedimientos (F10)

Paso a paso para salir (Shift +F11)

Paso a paso por instrucciones y Paso a paso por procedimientos sólo se diferencian en

la forma en que tratan las llamadas a funciones. Ambos comandos indican al depurador que

ejecute la siguiente línea de código. Si la línea contiene una llamada a una función, Ir a

instrucciones sólo ejecuta la llamada en sí y, a continuación, se detiene en la primera línea

de código incluida en la función. Paso a paso por procedimientos ejecuta toda la función y

después se detiene en la primera línea que está fuera de ella. Utilice Ir a instrucciones si

desea examinar el interior de la llamada a la función. Utilice Paso a paso por

procedimientos si desea evitar la ejecución paso a paso de las instrucciones incluidas en las

funciones.

Utilice Paso a paso para salir cuando esté dentro de una llamada a una función y desee

volver a la función que la invocó (por ejemplo si desde main fue a parar a una línea donde

hay un printf). Paso a paso para salir reanuda la ejecución del código hasta regresar a la

función y, a continuación, se interrumpe en el punto devuelto de la función de llamada.

Page 7: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

También puede ejecutar hasta el punto donde haya colocado el cursor en el editor de código

o hasta una función específica (a menos que antes exista un breakpoint, en cuyo caso irá

hasta él y se olvidará la posición de la línea que quería ejecutarse). Para ejecutar un proceso

hasta la ubicación del cursor, coloque el cursor en una línea de código ejecutable de una

ventana de código fuente. En el menú contextual del editor, elija Ejecutar hasta el cursor

(Ctrl-F10). Esta opción es una especie de breakpoint temporario. Resulta útil cuando se

comienza un debug y también puede usarse estando en el modo edición (no haciendo un

debug).

Siguiendo con el ejemplo, presionando F10 (Paso a paso por procedimientos) se ejecutará

la línea de código de scanf (de a y b). Aparecerá la ventana de comandos esperando que

ingrese dos números enteros separados por un espacio. Al pulsar Enter el control vuelve a

la ventana de código y la flecha amarilla se mueve a la siguiente sentencia a ejecutar.

Ahora en la ventana de inspección los valores de a y b aparecen en rojo indicando que

cambiaron durante la ejecución más reciente. Los valores también pueden observarse en

hexadecimal (menú contextual: Presentación hexadecimal).

Obviamente, los valores coinciden con los ingresados por teclado, por tanto esta línea de

código no tiene problemas. Si se pulsa nuevamente F10 (Paso a paso por procedimientos) y examina nuevamente la

ventana de inspección:

El valor de a aún permanece en rojo, indicando que su valor cambió en la reciente

ejecución (o sea en la sentencia: if(a=b) printf("Son iguales!\n");), en cambio b aparece

en negro indicando que durante dicha ejecución no cambió de valor. La variable a no sólo

cambió de valor sino que su valor actual es igual al de b, lo cual explica el problema.

Page 8: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

Observando con detalle esta sentencia puede verse que al comparar a con b en lugar de usar

el == se usó el símbolo de la asignación =.

Si en algún momento se quiere dejar de ejecutar por pasos, pulse F5 para reanudar la

ejecución. Si ya hubiese encontrado todos los errores y quiere detener la ejecución, en el

menú Depurar elija Detener depuración o pulse Shift+F5 o en la barra de herramientas .

Existe también una ventana de Variables locales donde se muestran las variables definidas

en el ámbito local, que generalmente es la función o el método que se encuentra en

ejecución.

Otra forma común de ver rápidamente el valor de una variable durante una depuración se

logra pasando el cursor sobre una variable; aparece una pequeña ventana indicando su valor

actual (Data Tip). Haciendo click con el botón derecho aparece este menú que, entre otras

opciones, permite editar valores (modificables).

Siguiendo con el ejemplo y, para buscar el otro error, suponiendo que el valor para la

variable c haya sido ingresado correctamente por teclado, coloque un punto de interrupción

en la siguiente línea: cAsPercent = toPercent(c);

En la ventana de inspección puede borrar las líneas correspondientes a a y b (o escribirle

encima) para ver las variables c y cAsPercent.

Page 9: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

La variable cAsPercent contiene basura puesto que no fue inicializada; si pulsamos F10 su

valor cambia a 0 indicando que, algo en la función toPercent no está funcionando

correctamente:

Desafortunadamente al utilizar la opción F10 (Paso a paso por procedimientos) la función

ya se ejecutó. Se deberá detener la ejecución, puede usar ejecutar hasta el cursor en

cAsPercent = toPercent(c);y, usar la opción F11 (Paso a paso por instrucción) para entrar

en dicha función. Añada a la ventana de inspección las variables decimal y result.

Ahora no se pueden inspeccionar las variables c y sAsPercent puesto que pertenecen a main

y se está ejecutando otra función. Si ejecutamos otro paso con F10 (no hay llamadas a

funciones, sólo una asignación), la variable result vale 0!

Si se analiza esta última línea ejecutada: result = (int)decimal * 100; puede verse que

decimal fue convertida (cast) a entero, por tanto se truncó su parte decimal antes de

multiplicarla por 100 para transformarla en un porcentaje.

Al ejecutar la línea: cAsPercent = toPercent(c); podemos visualizar la ventana de Pila de

llamadas (Alt+7 o desde Depurar/Ventanas/Pila de Llamadas) para ver dicha invocación a

toPercent desde main, además de observar qué parámetro real se le pasó a la función al

invocarla (interesante para ver si a una función se le pasó un puntero NULL),. Esto permite

añadir otra pregunta aparte de ¿Qué sucedió realmente?: ¿Cómo se llegó a un determinado

estado?, esto es especialmente importante en programas extensos o cuando se utilizan

funciones recursivas.

Utilizando la ventana Pila de llamadas, puede ver las llamadas a las funciones o

procedimientos que están actualmente en la pila. En la misma, se muestran el nombre de

cada función y el lenguaje de programación en el que se ha escrito. El nombre de la función

o del procedimiento puede ir acompañado de información opcional, como nombre de

módulo, número de línea, desplazamiento de byte, así como nombres, tipos y valores de

parámetro. La presentación de esta información opcional se puede activar o desactivar.

Page 10: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

Recordar que, en las ventanas de inspección, también puedo verificar el valor de

expresiones (aritméticas, casting, aritmética de punteros, etc.):

En el siguiente ejemplo extraído de la práctica 0 y que usa arrays:

#include <stdio.h> int main() { int vleidos[3], valor,i,j; for (i=0; i<3; i++){ printf("Ingrese un entero: "); scanf("\n %d",&valor); vleidos[i]=valor; printf ("El valor leido es: %d \n",valor); } while (valor != 0){ printf ("Ingrese un valor \n"); scanf(" \n %d",&valor); for (j=0; j<3; j++){ if (valor==vleidos[j]) printf (" Valor ocupa el lugar : %d del vector \n", j ); else continue; } } return 0; }

Si quiero usar la ventana de inspección para ver el contenido del array vleidos, antes de

ejecutar el while por ejemplo, se verá que, al ser el array un objeto complejo (al igual que

una estructura) y no un tipo de dato primitivo, a la izquierda del nombre del array aparece

Page 11: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

un +, el cual puede expandirse para ver cada uno de los elementos; además puede

apreciarse que en valor de vleidos, figura la dirección de memoria donde comienza el

elemento 0:

Lo mismo se obtiene pasando el mouse sobre el nombre del array:

Debug a bajo nivel

Hay otras ventanas que son usadas típicamente para hacer un debug a bajo nivel, cuando

todas las otras alternativas que ofrece el debugger ya fueron utilizadas. Son las ventanas de

Memoria, desensamblador y Registros, las cuales requieren un poco de conocimiento de

base y paciencia para analizar y utilizar la información que presentan las mismas.

La ventana Memoria (activada con ALT+6) permite ver el espacio de memoria que utiliza

la aplicación (tal vez sea la menos utilizada de las ventanas). La ventana Inspección y la

ventana Variables locales muestran el contenido de las variables, que se almacenan en

Page 12: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

ubicaciones específicas de la memoria pero, la ventana Memoria muestra la imagen a gran

escala. Esta visión puede ser conveniente para examinar grandes conjuntos de datos

(búferes de cadenas largas, por ejemplo), que no se muestran adecuadamente en las demás

ventanas. Sin embargo, la ventana Memoria no se limita a mostrar datos. Muestra todo lo

que hay en el espacio de memoria, ya sean datos, código o bits aleatorios de la memoria no

asignada.

La ventana Memoria solo está disponible si la depuración de nivel de dirección está

habilitada en el cuadro de diálogo Opciones de Depurar, y el nodo Depuración.

En este caso y en el ejemplo de array se observa el array vleidos. En el menú se puede

modificar la cantidad de bytes que se muestran por pantalla, la forma en que se interpretan

los datos (como enteros de 4 bytes, floats, etc.), la última columna son los caracteres ASCII

de las posiciones de memoria mostradas (salvo que haya texto almacenado en esas

posiciones, es basura). El cuadro Dirección no solo acepta valores numéricos sino también

expresiones que se evalúan como direcciones. De forma predeterminada, la ventana

Memoria trata una expresión de Dirección como una expresión en directo, que se vuelve a

evaluar durante la ejecución del programa. Las expresiones en directo pueden ser muy

útiles. Puede utilizarlas, por ejemplo, para ver la memoria a la que señala un puntero.

En este otro caso en la caja de texto indica &i (valor 3)

En la ventana Desensamblado (Alt+8) se muestra el código de ensamblado

correspondiente a las instrucciones creadas por el compilador. Además de las instrucciones

de ensamblado, la ventana Desensamblado puede mostrar la siguiente información

opcional:

Dirección de memoria donde se encuentra cada instrucción máquina. Para

aplicaciones nativas, ésta es la dirección de memoria real. Para Visual Basic, C#,

es un desplazamiento desde el inicio de la función.

Código fuente del que se deriva el código ensamblado.

Bytes de código: representaciones en bytes de las instrucciones máquina.

Nombres de símbolos para las direcciones de memoria.

Número de líneas correspondiente al código fuente.

Page 13: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

Las instrucciones en lenguaje de ensamblado consta de mnemónicos, que son abreviaturas

de nombres de instrucciones, y de símbolos que representan variables, registros y

constantes. Cada instrucción de código máquina se representa con un mnemónico de

lenguaje de ensamblado, normalmente seguido de una o más variables, registros o

constantes. Si no conoce el lenguaje de ensamblado pero desea aprovechar al máximo la

ventana Desensamblado, hágase con un buen libro sobre programación en lenguaje de

ensamblado.

Debido a que el código de ensamblado se refiere continuamente a los registros del

procesador, a menudo le resultará útil usar la ventana Desensamblado junto con la ventana

Registros, que permite observar el contenido de los registros.

Probablemente, nunca sentirá el deseo de ver las instrucciones de código máquina con su

formato puro, numérico, en lugar del lenguaje de ensamblado. Sin embargo, si así lo desea,

puede usar la ventana Memoria con este fin, o elegir Bytes de código en el menú contextual

de la ventana Desensamblado.

Podria utilizarse para comparar distintos bloques de códigos y observar cuál es más

eficiente, en cuanto a número de instrucciones de código de máquina que generan.

La ventana Registros (Alt+5) muestra el contenido de los registros. Si mantiene abierta la

ventana Registros mientras ejecuta el programa paso a paso, podrá ver cómo cambian los

valores de los registros mientras se ejecuta el código. Los valores que han cambiado

recientemente se muestran en rojo. Se pueden modificar los valores de los registros. Por

motivos de claridad, la ventana Registros organiza los registros en grupos que varían según

la plataforma y el tipo de procesador. Puede mostrar u ocultar grupos como desee.

Los registros son ubicaciones especiales dentro de un procesador (CPU) en las que se

almacenan pequeñas cantidades de datos con los que el procesador trabaja de forma activa

en cada momento. Cuando el código fuente se compila o se interpreta, se generan

instrucciones que transfieren datos de la memoria a los registros y viceversa, según sea

necesario. El acceso a los datos de los registros es muy rápido en comparación con el

acceso a la memoria, de modo que el código que permite al procesador mantener datos en

un registro para el acceso repetido a ellos suele ejecutarse más rápidamente que el código

que requiere que el procesador cargue y descargue constantemente los registros.

Los registros pueden dividirse en dos tipos: de propósito general y de propósito especial.

Los registros de propósito general contienen datos para operaciones generales, como sumar

Page 14: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

dos números o referirse a un elemento de una matriz. Los registros de propósito especial

tienen un fin específico y un significado especializado. Un buen ejemplo es el registro

puntero de pila, que el procesador utiliza para hacer un seguimiento de la pila de llamadas

del programa. Como programador, probablemente no controlará directamente el puntero de

pila. Sin embargo, este registro es esencial para el correcto funcionamiento del programa,

ya que sin el puntero de pila el procesador no sabría a dónde regresar al terminar una

llamada a una función.

La mayoría de los registros de uso general contienen sólo un elemento de datos único. Por

ejemplo un entero único, un número en punto flotante o un elemento de una matriz.

Algunos de los procesadores más actuales cuentan con unos registros de mayor tamaño,

llamados registros vectoriales, que pueden contener una pequeña matriz de datos. Debido a

su mayor capacidad, los registros vectoriales permiten realizar operaciones con matrices a

gran velocidad. Los registros vectoriales comenzaron a utilizarse en equipos de gran

tamaño, caros y de alto rendimiento, pero comienzan a estar disponibles en los

microprocesadores, donde se usan con gran provecho en las operaciones gráficas

intensivas.

Normalmente, un procesador tiene dos conjuntos de registros de propósito general, unos

optimizado para operaciones en coma flotante, y otros para operaciones con números

enteros. Como es lógico, los registros de estos grupos reciben el nombre de registros de

coma flotante y registros de enteros, respectivamente.

Page 15: Debug en Microsoft Visual C++ (2010) Express Editionejecutar el código por pasos. El menú Depurar contiene tres comandos para avanzar paso a paso por el código: Paso a paso por

Al seguir ejecutando sentencias pueden observarse los registros y flags que cambiaron (se

muestran en rojo)

Referencias

Guía básica del depurador de Visual Studio https://msdn.microsoft.com/es-

es/library/k0k771bt(v=vs.100).aspx

Getting Started with the Visual C++ Debugger

http://digital.cs.usu.edu/~allanv/cs2420/debugger_intro.pdf