Introducción a los Computadores Práctica nº 1 Introducción al Sistema Operativo Windows. Windows es un sistema operativo basado en un entorno gráfico desarrollado por Microsoft. El interfaz que Windows presenta al usuario gira alrededor de un Escritorio o mesa de trabajo que simula las condiciones de trabajo de nuestro entorno habitual. Sobre dicho Escritorio podemos situar las herramientas que utilizamos con mayor frecuencia (representadas por iconos o dibujos que ilustran su contenido) y manejarlas con simples movimientos del ratón. Además, un sistema de menús despegables nos permite acceder rápidamente a cualquiera de las aplicaciones instaladas en nuestro ordenador. Windows es un sistema operativo multitarea, permite tener activas varias aplicaciones de forma simultánea y posibilita el intercambio de información entre las mismas mediante el Portapapeles y otros mecanismos. La Barra de Tareas situada en la parte inferior de la pantalla proporciona un acceso rápido a las aplicaciones activas en el sistema. Como entrar en el sistema: Al arrancar el sistema, este pedirá que nos identifiquemos para poder empezar a trabajar. Usuario: Alumno Password: Como arrancar un programa. Para arrancar una aplicación: 1. Pulsar sobre el botón Inicio de la barra de tareas. 2. Situar el ratón sobre los diversos menús desplegables hasta encontrar la aplicación. 3. Ejecutar la aplicación pulsando el botón izquierdo sobre el icono de la aplicación. Otra forma de arrancar una aplicación es con el comando Ejecutar del menú Inicio. En este caso se puede escribir directamente el nombre de la aplicación y el camino completo de acceso o puede usarse la opción Examinar para buscarla mediante el explorador. También se puede arrancar una aplicación pulsando dos veces con el botón izquierdo en su icono de acceso directo del escritorio, si se ha definido para esa aplicación. Pág. 1 Introducción a los Computadores Práctica 1 Apagar y reiniciar el equipo. En el menú Inicio de la barra de tareas se encuentra la opción de Apagar el sistema, que debe usarse cuando queramos apagar o reiniciar el ordenador. Cómo obtener ayuda. En la mayoría de los programas basta con pulsar la tecla <F1> para obtener ayuda sobre el programa activo. Otra forma de conseguirla es seleccionar la opción Ayuda o Help en la barra de menús del programa en cuestión. Para obtener ayuda general sobre el propio Windows selecciona la opción Ayuda del menú Inicio de la Barra de Tareas. Ficheros o archivos La información existente en las unidades de almacenamiento del sistema (disquetes o discos duros) está organizada en "carpetas" que contienen "ficheros" o "archivos", con objeto de facilitar su localización y manejo no sólo al propio sistema operativo sino también a otros programas que se ejecuten en el ordenador y al usuario del mismo. Para poder identificar el contenido guardado en un fichero, y por tanto facilitar su localización, los archivos poseen una identificación o denominación que debe ser dada por el usuario en el momento de su creación. En Windows los ficheros se identifican de la forma: "Nombre.Extensión" En donde NOMBRE identifica al nombre del archivo y puede estar formado por hasta 255 caracteres, que pueden ser letras, números y símbolos de puntuación a excepción de los siguientes: (* \ | : . , = < > + ? ; /). Es conveniente utilizar nombres de ficheros que faciliten la identificación del contenido de los mismos. También hay que tener cuidado de no utilizar el mismo nombre para dos archivos diferentes en la misma carpeta, ya que el sistema operativo o bien no aceptará el segundo fichero o incluso podría destruir la información del primero para sustituirla por la del segundo. La EXTENSION de un fichero identifica el tipo de contenido del mismo. La extensión empieza por un punto "." seguido normalmente de hasta 3 caracteres, con las mismas limitaciones que para los nombres. Pero si bien el nombre es obligatorio, la extensión es opcional, de tal forma que puede haber ficheros que en su denominación no posean extensión, aunque no es muy recomendable, ya que la extensión va a permitir al sistema catalogar diferentes tipos de ficheros y asociarlos a determinadas aplicaciones que los manejan. En Windows, los archivos de datos normalmente tienen asociados una aplicación. Cuando se abre un archivo que no es ejecutable, se ejecuta previamente la aplicación a la que está asociado y seguidamente se abre dicho fichero sobre la aplicación. Esta asociación se hace a través de la extensión del fichero. Así, por ejemplo: Extensión .doc .txt .cpp .exe .dll Tipo de fichero Fichero con un documento que se abre con el procesador de textos Word. Fichero de texto que se abre con el programa Bloc de Notas. Fichero fuente en C++ que se abre con el programa Dev-C++. Fichero directamente ejecutable. No está asociado a ninguna aplicación. Fichero con librerías del sistema. Son usados directamente por el S.O.. Cuando algún programa, u orden del sistema operativo, necesita trabajar con un fichero es necesario especificarlo escribiendo su identificación: nombre y extensión. Aunque numerosos programas no necesitan que se les indique la extensión, asumiendo que el nombre indicado posee una extensión predeterminada y conocida por dicha aplicación. Así, en el traductor de Dev-C++ no es necesario indicar la extensión si el fichero posee como extensión ".cpp" (extensión predeterminada para dicho traductor). De igual manera, al generar un fichero de datos con una aplicación, normalmente la extensión es añadida automáticamente por la misma. Pág. 2 Introducción a los Computadores Práctica 1 El explorador de Windows. Para trabajar con archivos podemos usar el explorador de Windows. El explorador es una aplicación que nos permite movernos por las carpetas del sistema para acceder a los archivos guardados en el disco. Para ejecutar el Explorador tenemos que activar su icono el submenú Programas del menú Inicio de la barra de tareas. El área de trabajo del explorador se divide en dos zonas. En la zona izquierda se muestra el árbol completo de carpetas del escritorio y en la izquierda el contenido de la carpeta activa. Puede cambiarse de ventana activa moviéndose con los cursores o pulsando con el botón del ratón. Una carpeta puede tener a su vez un árbol de carpetas que cuelgan de ella. Para abrir el árbol de carpetas basta con pulsar con el botón el símbolo ‘+’ que aparece a la izquierda de la carpeta. Si se quiere volver a cerrar el árbol, basta con pulsar el símbolo '-' que aparece a la izquierda de la carpeta. También se puede abrir pulsando la tecla <Flecha derecha> sobre la carpeta activa y cerrar pulsando la tecla <Flecha Izquierda> sobre la carpeta activa. Para volver a la carpeta anterior hay que pulsar la tecla <Retroceso>. Se puede seleccionar un archivo o un grupo de ellos. Para seleccionar un archivo basta con pincharlo con el botón izquierdo del ratón. Si se quiere seleccionar un grupo de archivos contiguos, se selecciona el primero y, manteniendo pulsada la tecla <Mayús.>, se selecciona el último archivo del grupo. También puede hacerse arrastrando el ratón hasta incluir los dos extremos del grupo de archivos seleccionados. Si los archivos no están contiguos, se seleccionan individualmente manteniendo pulsada la tecla <Control>. Operaciones con archivos y carpetas. Para crear una nueva carpeta, hay que elegir la opción Carpeta del submenú Nuevo del menú Archivo. Pág. 3 Introducción a los Computadores Práctica 1 Para acceder a los menús con el teclado, hay que pulsar la tecla <Alt.> a la vez que la letra que aparece subrayada en el nombre de la opción del menú. Para elegir una opción basta con pulsar la letra subrayada del nombre. Una vez creada la carpeta hay que darle un nombre. Los nombres de las carpetas y de los archivos pueden cambiarse. Para ello, hay que seleccionar previamente el archivo y luego pulsar el botón derecho del ratón, aparecerá el llamado menú contextual que nos permitirá seleccionar las operaciones más comunes sobre ese archivo o carpeta. Seleccionaremos la opción Cambiar nombre y entonces podremos escribir el nuevo nombre, se valida el cambio pulsando <Intro>. Si se quiere cancelar el cambio, se pulsa <Esc.> en vez de <Intro.>. También se puede hacer mediante la opción Cambiar Nombre del menú Archivo. Para copiar un archivo se selecciona y se arrastra a la nueva carpeta manteniendo pulsados la tecla <Control> y el botón izquierdo. Al llegar a la nueva carpeta se sueltan ambos. También puede copiarse un archivo usando las opciones del menú Editar. Una vez seleccionado el archivo a copiar, se escoge la opción Copiar del menú Editar. Luego, se selecciona la carpeta de destino y se escoge la opción Pegar del menú Editar. Para copiar un archivo a disquete podemos utilizar el procedimiento anterior o de forma más simple usar el menú contextual del archivo. Para ello seleccionamos el archivo y pulsando el botón derecho del ratón, situamos entonces el ratón encima de la opción Enviar a y se desplegará un nuevo menú en el que seleccionamos Disco de 3 ½ A. Para mover un archivo de una carpeta a otra, la operación es muy similar. Se selecciona el archivo y se arrastra a la nueva carpeta con el botón izquierdo pulsado. Para realizar la operación con las opciones del menú Editar, se selecciona el archivo, se elige la opción Cortar del menú Editar, se selecciona la nueva carpeta y se elige la opción Pegar del menú Editar. Hay varias formas para eliminar un archivo. Se puede pulsar la tecla <Supr.> una vez seleccionado el archivo o elegir la opción Eliminar del menú Archivo. En ambos casos hay que confirmar la eliminación. También puede arrastrarse el icono del archivo directamente hasta el icono de la papelera de reciclaje situado en el escritorio o el situado en la parte izquierda de la ventana del explorador. Búsqueda de archivos o carpetas. También el explorador facilita la búsqueda de archivos, pero dependiendo de la versión de Windows utilizada la forma de hacerlo difiere. El unos casos hay en la parte superior del explorador un botón denominado Búsqueda, en otros activaremos la opción Archivos o Carpetas del submenú Buscar en el menú Herramientas del explorador. Se pude extender la búsqueda por todo el árbol de carpetas o especificar la carpeta de inicio para la búsqueda. El nombre del archivo a buscar puede incluir símbolos comodines Existen dos símbolos que se pueden utilizar para sustituir caracteres en la denominación de un fichero o carpeta con algunos comandos del sistema (como por ejemplo el de Búsqueda) o con algunas aplicaciones. Son los llamados símbolos "comodines". Estos símbolos no pueden formar por tanto parte del nombre o extensión de los archivos, y su utilización se limita a la referencia a los mismos. Los símbolos comodines son: * (asterisco) e ? (interrogación) El símbolo * (asterisco) sustituye todos los caracteres que falten en el nombre del archivo o en su extensión (dependiendo de donde se encuentre dicho símbolo, si en el nombre o en la extensión, o en ambos). Así por ejemplo: a*.* *.cpp *.* Es una denominación que incluye todos los ficheros cuyo nombre empiece por la letra "a" y posean cualquier extensión, o que incluso no la posean. Es una denominación que incluye todos los ficheros de cualquier nombre y con extensión ".cpp". Es una denominación que incluye todos los ficheros de cualquier nombre y cualquier extensión, incluso aquellos que no posean extensión. El símbolo ? (interrogación) sustituye solamente un carácter, el que se encuentra en la posición del símbolo, y puede utilizarse tanto en el nombre como en la extensión. Por ejemplo: pro?1.b?? Es una denominación en la que se encuentran incluidos ficheros tales como prog1.bxy, prob1.bhj, etc. Pág. 4 Introducción a los Computadores Práctica 1 Los dos símbolos (* y ?) pueden combinarse en la denominación de ficheros. Como por ejemplo: *.?pp Incluye todos los archivos de cualquier nombre cuya extensión tenga tres caracteres y termine por las letras "pp". Como por ejemplo "hola.cpp", "adios.ppp", "p1.app", etc. Cuando los ficheros han sido denominados de una forma lógica y coherente, la utilización de comodines evitará teclear muchos caracteres a la hora de escribir la denominación de uno o más ficheros, permitiendo incluso realizar algunas operaciones sobre un conjunto de ficheros relacionados en vez de tener que hacerlo de uno en uno. Abrir archivos. Para ejecutar una aplicación desde el explorador, basta con pulsar dos veces sobre su icono. También se puede ejecutar con la opción Abrir del menú de contexto (que se abre con le botón derecho sobre le icono) o del menú Archivo. Cuando se abre un archivo que no es ejecutable, previamente se abrirá la aplicación a la que está asociado y sobre la misma se abrirá el fichero de datos. Si un tipo de fichero no tiene asociada ninguna aplicación, el sistema pide que se le asocie una al abrirlo. Esta asociación permanece para las próximas veces que se abra un archivo de ese tipo. Se puede eliminar la asociación con la opción Tipos de Archivo del submenú Opciones del menú Ver. Papelera de reciclaje. Cuando un archivo se borra, no se pierde del sistema, sino que se manda a la papelera de reciclaje. La papelera de reciclaje permite recuperar ficheros eliminados (seleccionando los ficheros deseados y escogiendo la opción Restaurar del menú Archivo) o borrarlos definitivamente (opción Eliminar del menú Archivo). Entorno de programación Dev-C++. Dev-C++ es un entorno de programación (IDE) para los lenguajes C y C++ totalmente gratuito (licencia de software de dominio público GPL) desarrollado por miembros del GNU (Bloodshed Software). Dev-C++ funciona bajo el entorno Windows y permite generar programas ejecutables para DOS (modo consola) y para Windows (95 / 98 / NT 4 / 2000 / XP). También incorpora un depurador integrado para C++. Para arrancar el entorno Dev-C++ habrá que seleccionar el correspondiente icono de la carpeta "Programas", en el menú "Inicio" de Windows, y dentro de la subcarpeta llamada "Bloodshed Dev-C++". Aparecerá una ventana de presentación, y transcurridos unos instantes se presentará la pantalla principal del entorno. Pág. 5 Introducción a los Computadores Práctica 1 En esta pantalla observamos un menú en la parte superior con las siguientes opciones: • • • • • • • • • • • Archivo. Comandos de manejo de ficheros fuente y proyectos como "Nuevo", "Abrir Proyecto o Archivo", "Guardar", "Cerrar", "Imprimir", "Importar", "Exportar", etc. Edición. Comandos para alterar y editar los programas fuentes en el editor integrado, como "Cortar" texto, "Pegar" texto, "Deshacer" cambios, etc. Buscar. Opciones para localizar y cambiar variables u otra información en los programas fuente. Ver. Los menús y las barras de herramientas (Toolbars) pueden activarse o desactivarse. Proyecto. Comandos para manejar proyectos con múltiples ficheros fuente y para cambiar las opciones del proyecto. Ejecutar. Contiene los comandos para compilar y ejecutar los programas. Depurar. Comandos para manejar el depurador integrado. Herramientas. Permite cambiar la configuración por defecto del entorno de programación y el compilador, así como utilidades para manejar e instalar herramientas y librerías externas. CVS. Comandos para manejar el Control de Versiones de ficheros Fuente integrado en el entorno. Ventana. Opciones para cambiar de ventanas y configurar la organización de las mismas dentro del entorno. Ayuda. Información de ayuda sobre el entorno y un pequeño tutor de lenguaje C (en inglés). También podemos distinguir una serie de barras de botones en la parte superior, que nos permitirán activar de forma directa muchas de las opciones de los menús. En la parte inferior destaca una línea de estado y una serie de pestañas que nos permitirán ver información sobre el proceso de compilación de nuestro programa y los posibles errores detectados por el compilador. Una vez arrancado el entorno, para comenzar un nuevo programa, pulsamos en el icono Código Fuente, o bien seleccionamos Código Fuente en la opción Nuevo del menú Archivo, se generará un fichero fuente nuevo y aparecerá la ventana del editor con el esqueleto de un programa fuente en C++. Pág. 6 Introducción a los Computadores Práctica 1 Obsérvese los diferentes colores usados por el editor para destacar los distintos elementos sintácticos del programa, los cuales también aparecerán según escribamos líneas de código. Sobre dicho esqueleto escribiremos el siguiente programa: /*---------------------------------------------------------------| Autor: | | Fecha: Versión: 1.0 | |-----------------------------------------------------------------| | Descripción del Programa: holamundo.cpp, entrada salida | | Primer programa de ejemplo en C++ para saludar al mundo. | | ----------------------------------------------------------------*/ // Incluir E/S y Librerías Standard #include <iostream> // Permite el uso de la E/S estándar #include <cstdlib> // Permite el uso de funciones estándar using namespace std; // Zona de Declaración de Constantes // Zona de Declaración de Tipos // Zona de Cabeceras de Procedimientos y Funciones // Programa Principal int main() { // Zona de Declaración de Variables del Programa principal // Zona de instrucciones cout << "Hola mundo." << endl; // Presenta texto literal y salta línea system("PAUSE"); return 0; // Espera a pulsar una tecla // Valor de retorno a S.O. } // Implementación de Procedimientos y Funciones Pág. 7 Introducción a los Computadores Práctica 1 En la parte superior, donde pone autor y fecha, escribe a continuación tu nombre y la fecha de hoy. Con esto ya tenemos nuestro primer programa. A continuación debemos guardar el fichero fuente de nuestro programa. Para ello seleccionaremos la opción Guardar del menú Archivo (o el icono Guardar de la barra de botones) y aparecerá una ventana solicitando un nombre de archivo para el programa. En primer lugar deberemos localizar y situarnos en el directorio que anteriormente hemos creado "ITI_1", ayudándonos del explorador simplificado que aparece en esta ventana. También cambiaremos el nombre propuesto como SinNombre1 por el nombre que queramos darle al programa, por ejemplo “holamundo.cpp” y pulsaremos sobre el botón Guardar. Nótese que todos los programas fuente en C++ deben acabar siempre en .cpp El preprocesador. Analizando nuestro programa podemos observar unas líneas al principio del código en color verde. Son directivas o cláusulas para el preprocesador. ¿Y que es el preprocesador? El preprocesador, es un programa integrado en el compilador, que se ejecutará de forma automática al compilar nuestro programa y que realiza una serie de tareas previas al proceso de compilación. Mediante el preprocesador podemos: • • • • • Incluir ficheros de texto en nuestro programa fuente. Definir macros. Definir bloques de compilación condicional. Establecer parámetros dependientes de la máquina o compilador concreto. Etc. En nuestro caso, la directiva usada "#include" incluye en nuestro programa unos ficheros que contienen las definiciones necesarias para poder usar una serie de bibliotecas del sistema. Son los llamados ficheros de cabecera (header en inglés), como por ejemplo: • iostream, contiene los operadores básicos para poder usar la entrada/salida estándar (como por ejemplo el objeto "cout"). Pág. 8 Introducción a los Computadores • • • • Práctica 1 cstdlib, contiene una serie de operadores y funciones estándar de C++ (como por ejemplo la función "system"). math, contiene funciones matemáticas en punto flotante para números reales. iomanip, contiene los manipuladores de formato para la entrada/salida estándar. Etc. El preprocesador buscará estos ficheros en los directorios indicados en el apartado "Includes C++" de la pestaña "Directorios" en la ventana "Opciones del Compilador " que se activa desde la opción "Opciones del Compilador" del menú "Herramientas". Es importante no cambiar nunca estos directorios. No se debe utilizar nunca la directiva "#include" para insertar ficheros con código de programa C++. Compilación y ejecución del programa. El siguiente paso es compilar nuestro programa para crear el código objeto y enlazarlo con las bibliotecas del sistema, para generar el programa ejecutable. Para ello seleccionaremos la opción Compilar del menú Ejecutar (o pulsando el botón Compilar en la barra de botones). Esta opción no solo compila nuestro programa sino que también lo enlaza con las bibliotecas del sistema necesarias para generar un programa ejecutable. Al compilar, aparecerá encima del editor una pequeña ventana con el transcurso del proceso de compilación y al final del mismo aparecerán los resultados del mismo, indicando el número de errores encontrados. Una vez finalizado el proceso pulsaremos en botón Cerrar. Para ver una información más completa sobre el proceso de compilación podemos pulsar sobre la pestaña Resultado de la compilación que aparece en la parte inferior de la pantalla. Se desplegará una ventana informándonos sobre los parámetros de la compilación y el resultado de la misma, el número de errores encontrado y el tamaño del fichero ejecutable en bytes. Para cerrar la ventana basta con pulsar en la pestaña Cerrar. Pág. 9 Introducción a los Computadores Práctica 1 En caso de producirse errores, se detendrá el proceso de compilación, la ventanita que muestra el transcurso de la compilación desaparecerá y se desplegará la pestaña Compilador en la parte inferior de la pantalla mostrándose los diferentes mensajes de error. Pulsando con el ratón sobre uno de estos mensajes aparecerá destacada (en color rojo oscuro) la línea del programa fuente en donde el compilador ha detectado el correspondiente error. Si todo ha ido bien, y no se han detectado errores, podemos observar que en el directorio en donde hemos guardado el fichero fuente aparece un nuevo fichero "holamundo.exe", se trata del programa ejecutable. Ya podemos ejecutar nuestro programa. Para ello tenemos dos alternativas o pulsar sobre el botón Ejecutar de la barra de botones o bien seleccionar la opción Ejecutar del menú Ejecutar. Pág. 10 Introducción a los Computadores Práctica 1 Aparecerá entonces una ventana MS-DOS con el siguiente contenido: Al pulsar una tecla la ventana se cerrará y volveremos al entorno de Dev C++. Salvo que el programa se interrumpa, la ejecución empezará por la primera sentencia de main y terminará por la última. Al finalizar de ejecutarse el programa, la ventana MS-DOS desaparece. Obsérvese que el programa presenta el mensaje “Presionar cualquier tecla para continuar . . .” y se detiene gracias a la instrucción “system("PAUSE");”. Si no hubiéramos puesto esta instrucción., la ejecución del programa hubiera sido tan rápida que nos impediría ver la salida del mismo, la ventana MS-DOS haría un flash apareciendo para inmediatamente desaparecer. Prueba y depuración de programas. El propósito de la prueba y depuración de programas es establecer unos métodos más o menos formales para poder asegurar la corrección y fiabilidad de un programa fuente o algoritmo, con lo cual se puede valorar y mejorar la calidad del mismo. El único y real objetivo de la prueba de programas es el descubrir errores en los mismos. Los errores ocurren cuando cualquier aspecto de un algoritmo o programa es incompleto, inconsistente o incorrecto. Las tres grandes clases de errores en programación son los de requisitos, de diseño y de implementación. Los errores de requisitos se provocan por una propuesta incorrecta del problema. Los errores de diseño se introducen por fallos al traducir los requisitos a estructuras de solución correctas y completas. Los errores de implementación son los cometidos al traducir los resultados del diseño a programas fuente. Los dos primeros tipos de errores son bastante difíciles de detectar, y si no se descubren hasta las pruebas de los programas fuente, pueden ser muy costosos de corregir. La terminología sobre las pruebas de programas es algo confusa e inestable. Sin embargo podemos definir como PRUEBA DE UN PROGRAMA a aquel conjunto de actividades encaminadas a obtener confianza de que éste sea aceptable y correcto, aunque la prueba de un programa no puede asegurar la ausencia total de defectos, solamente puede demostrar su existencia. Como dice la ley de Gilb: "Todos los programas reales contienen errores mientras no se demuestre lo contrario, lo cual es formalmente imposible". Pág. 11 Introducción a los Computadores Práctica 1 El problema fundamental de las pruebas de programas es el encontrar procedimientos para seleccionar un conjunto de casos de prueba apropiados (datos de entrada del programa para efectuar la prueba), junto con la interpretación de sus resultados, que permitan inferir dicha confianza. Un buen caso de prueba es aquel que tiene una alta probabilidad de mostrar un error no descubierto hasta entonces. Esta selección se debe basar en la especificación e implementación del programa, y por tanto, una especificación o diseño inadecuado puede hacer imposible realizar pruebas significativas. Y cómo decidir cuando los resultados de una prueba son correctos o no. Esta es la hipótesis del "ORACULO", que indica que debe existir algún mecanismo (como por ejemplo la especificación formal del programa) que pueda predecir los resultados esperados de una prueba para poder contrastarlos con los obtenidos realmente en la misma. Y excepto en casos muy concretos en que existe una especificación formal (como es el caso de algoritmos matemáticos), lo normal es que no exista tal oráculo, y actué como tal el propio autor del programa (asumiendo un papel de "adivino"). Sin embargo, y aun dada la baja fiabilidad de un oráculo humano, este es el tipo de pruebas más utilizado. Una de las técnicas más sencillas de pruebas basadas en oráculo es el método de "PRUEBA Y ERROR", consistente en comprobar el resultado correcto y conocido ofrecido por el programa para una serie de datos de entrada. Será la técnica que emplearemos para probar nuestros programas. Los casos de prueba se deben escoger para unos valores de entrada típicos para los cuales se conocen los resultados esperados, además de valores límites (mínimo y máximo), valores especiales para los cuales el tratamiento es diferente y finalmente valores inaceptables para determinar la robustez del programa. Así, por ejemplo, si queremos probar un programa que calcula la raíz cuadrada de un número entero dando como resultado también un número entero, podríamos elegir como casos de prueba (datos de entrada) los siguientes: 4, 9, 1000000 (sabemos que los resultados deben ser 2, 3 y 1000 respectivamente), 0, 1 (tienen un tratamiento especial y los resultado deben ser también 0 y 1) y -4 (es un valor inaceptable, ya que el resultado no es entero sino imaginario y el programa deberá actuar en consecuencia). La depuración aparece como una consecuencia de la prueba de un programa, ya que cuando para unos determinados datos de entrada (un caso de prueba) se descubre un error, es necesario aislarlo y corregirlo, labor denominada depuración (del inglés "debbuging": "quitar bichos"). El proceso de depuración requiere una combinación de una evaluación sistemática junto con una gran dosis de intuición (ojo clínico para curar al programa). El enfoque más utilizado para realizar la depuración se basa en el concepto de búsqueda progresiva. Los casos de prueba relacionados con la ocurrencia de un error son organizados para aislar las posibles causas, llegando a definir diferentes "hipótesis de causa". Se usan dichos casos de prueba para probar o revocar cada una de las hipótesis; si alguna de las pruebas indica que alguna de las hipótesis parece cumplirse, se rechazan el resto y se refinan los casos asociados a dicha hipótesis, volviendo a realizar el proceso de partición y prueba para intentar aislar la causa del error. Dicho de otra forma, este enfoque consiste en desarrollar una lista de todas las posibles causas del error y realizar pruebas para eliminar cada una de ellas hasta llegar a la causa original. Así, por ejemplo, si se apaga de repente la luz de la clase, pueden existir diferentes causas de la avería, como por ejemplo: que se ha fundido la lámpara, o que ha saltado el diferencial de la planta, o se han fundido los fusibles del edificio, o que se trata de un apagón general. Para ir eliminando cada una de las hipótesis realizo pruebas sobre cada una de ellas; así para eliminar la de apagón general, miro por la ventana y si veo luz en la calle descarto dicha hipótesis. Si alguna de las pruebas ratifica una de las hipótesis (por ejemplo, miro por la ventana y veo que está todo a oscuras), subdivido dicha hipótesis en otras más concretas e intento volver a probarlas. Así por ejemplo apagón sólo en la zona, apagón en toda la ciudad, etc. Existe también otro enfoque de depuración denominado de análisis regresivo. En este enfoque se utiliza uno cualquiera de los casos de prueba que producen el error. Una vez producido se detiene el programa y se intenta seguir hacia atrás el programa en busca de la causa del error (análisis hacia atrás), o se examina el estado del programa en ese momento para intentar determinar la causa del error (volcado post-mortem). Sin embargo, ambos enfoques de búsqueda progresiva o de análisis regresivo no son excluyentes, utilizándose ambos conjuntamente. Pág. 12 Introducción a los Computadores Práctica 1 Como ayuda al proceso de depuración existen herramientas software, llamadas DEPURADORES (debuggers), que permiten realizar operaciones de control sobre la ejecución del programa en pruebas, tales como la ejecución instrucción por instrucción (llamada ejecución paso a paso del programa, o en inglés step by step), la detención del programa en puntos críticos (llamados puntos de ruptura o inglés breakpoints), la consulta y registro de los pasos efectuados por el programa (técnica llamada de puntos de traza o en inglés tracepoints o viewpoints), la consulta y modificación del valor de las variables del programa, etc. La utilización de este tipo de herramientas de ayuda a la depuración ofrece una serie de técnicas para poder inspeccionar y controlar las pruebas. • Una de las más utilizadas es el llamado "METODO DE TRAZA DE PROGRAMAS o de EJECUCION PASO A PASO", consistente en ir siguiendo la ejecución de cada paso o instrucción de alto nivel del programa para determinados casos de prueba, determinando en cada paso el estado interno del propio programa (como valores de variables, profundidades de anidamientos, subprogramas, memoria, etc.). Este método es el que hemos utilizado en clase para seguir o trazar manualmente la ejecución de un programa, observando los valores que van tomando las variables. Y además de ser útil para detectar errores también es muy útil para comprender el funcionamiento de un programa. • Otra técnica similar a la anterior pero no tan costosa es la de "PUNTOS DE RUPTURA (BREAKPOINTS)", consistente en incorporar al propio programa, en sitios clave (en los que se supone se encuentra la causa del error, o que se encuentran cerca de la misma), instrucciones que permitan detenerlo y analizar su estado interno (sus variables, etc.). De esta forma se investigan fragmentos de programa en vez de instrucciones individuales. Normalmente la técnica de breakpoints se usa conjuntamente con la de ejecución paso a paso. Se sitúan puntos de ruptura antes de la zona sospechosa, se ejecuta el programa del tirón hasta el punto de ruptura y a partir de ese momento se ejecuta el fragmento de código sospechoso paso a paso, observando el valor que van tomando las variables y otros elementos del estado del programa. • Una variante del método de puntos de ruptura es el de "PUNTOS DE TRAZA (TRACEPOINTS, VIEWPOINTS o WATCHPOINTS)", en el que las instrucciones introducidas no detienen la ejecución del programa, sino que simplemente informan (al pasar por dichos puntos) del estado interno del mismo. Uso del depurador. Para aprender el uso del depurador vamos a utilizar el programa "depuracion.cpp" que hemos introducido en el último ejercicio. Deberemos compilar el programa y corregir los posibles errores detectados por el compilador, ya que el depurador solo lo podremos usar después de compilar sin errores nuestro programa. El depurador (debugger) es una herramienta que nos va a ayudar a probar nuestro programa para descubrir posibles errores lógicos y de ejecución (aquellos que no pueden ser detectados por el compilador), permitiéndonos ejecutar línea a línea nuestro programa, mientras que vemos el valor que toman las variables y el flujo de ejecución. Para arrancar el depurador, tendremos que pulsar sobre la pestaña Depurar de la parte inferior de la pantalla, esto desplegará la ventana de depuración. Las distintas opciones del depurador son relativamente simples, y al igual que en Dev-C++ muchas de las opciones de menú más utilizadas tienen un botón asociado, que en este caso se encuentran en la ventana de depuración. La opción Depurar (Tecla F8) comienza la ejecución del programa desde el principio del mismo. Sin embargo, esto hará que el programa se ejecute completamente sin permitirnos observar su comportamiento. Para detener la ejecución del programa nos podemos servir de los puntos de ruptura o breakpoints. Son puntos donde la ejecución del programa se detendrá y que podremos aprovechar para inspeccionar el valor de variables, funciones, etc. Pág. 13 Introducción a los Computadores Práctica 1 Para añadir un punto de ruptura, simplemente situaremos el cursor en la instrucción (en la ventana de edición) en la que queremos que se detenga el programa y a través del menú contextual (botón derecho del ratón) o mediante la opción del menú Depurar, seleccionaremos “Añadir/Quitar Punto de Ruptura”. La línea en cuestión se destaca en color rojo y en el margen izquierdo aparece una marca también roja. Otra forma más rápida de añadir un punto de ruptura es simplemente pinchar con el ratón en el margen izquierdo a la altura de la línea en la que queremos añadir el punto de ruptura. Podemos poner tantos puntos de ruptura como queramos. Para quitar un punto de ruptura programado bastará con volver a pulsar con el ratón sobre la marca roja del margen izquierdo, con lo cual desaparecerá. O también situando el cursor en la línea en cuestión y mediante el menú contextual o bien mediante la opción del menú Depurar, seleccionar otra vez la opción “Añadir/Quitar Punto de Ruptura”. Ya podemos empezar a depurar nuestro programa. Para ello pulsamos el botón Depurar (o la tecla F8), el programa empezará a ejecutarse automáticamente desde la primera línea, la ventana MS-DOS de ejecución aparece normalmente, pero el programa se detendrá en el primer punto de ruptura que se encuentre, antes de ejecutar la instrucción marcada con el breakpoint. Esto lo podemos comprobar porque la línea correspondiente a dicho punto de ruptura cambia a color azul. Dicha línea azul marca la próxima instrucción a ejecutar. También podemos observar que los botones de la pestaña Depurar se activan, permitiéndonos realizar las siguientes operaciones: • Depurar. Arranca el depurador, restableciendo las condiciones iniciales. Pág. 14 Introducción a los Computadores • • • • Práctica 1 Parar ejecución. Finaliza la sesión de depuración y la ejecución del programa, volviendo a la situación inicial. Siguiente Paso. Ejecuta la instrucción en curso (la marcada de azul) y avanza a la siguiente instrucción, deteniéndose. Podemos observar que el color de resalte pasa a la siguiente instrucción. Avanzar Paso a Paso. Trabaja de forma parecida a “Siguiente Paso”, ejecutando la siguiente instrucción, la diferencia es que en caso de existir subprogramas, toma la llamada a estos como una sola instrucción y ejecuta completamente el subprograma. Saltar Paso. Ejecuta de golpe las instrucciones a partir de la instrucción actual hasta el siguiente punto de ruptura o hasta el final de programa. Otra forma de empezar a ejecutar nuestro programa es situar el cursor encima de la primera línea en la que queremos que se detenga y pulsar el botón “Ir a cursor” o la opción de igual nombre del menú contextual o del menú Depurar. El programa arrancará desde el principio y se detendrá en la línea que hayamos marcado. Una vez arrancado el depurador, será útil tener visibles las variables locales (aquellas que estamos usando en cada momento) y poder observar los valores que van tomando, para ello las buscamos en la ventana del editor, las seleccionamos y a continuación abrimos el menú contextual (botón derecho del ratón), en donde veremos la opción “Añadir watch” (o también podemos usar la opción de igual nombre del menú Depurar en la barra principal de menús, o pulsando la tecla F4, o con los botones que hay a la derecha de la ventana de depuración). Al seleccionar la opción, aparecerá en la pestaña “Depurar” de la zona izquierda de la pantalla, el nombre de dicha variable. Otra forma rápida de añadir un watch es haciendo doble clic sobre el nombre de la variable deseada. Para eliminar una variable observada, basta con seleccionarla en la ventana Depurar y pulsar el botón “Quitar watch” o presionar la tecla SUPR. La opción Añadir watch no solo nos permite observar el valor de las variables sino incluso de expresiones. También podemos observar que en la lista de variables observadas de la pestaña Depurar aparecen unos valores para las mismas. ¿Y por qué la variable curso vale 4370436? Esto se debe a que TODA VARIABLE TIENE UN VALOR INDEFINIDO ANTES DE SER INICIALIZADA, y todavía no hemos inicializado o recogido el valor de dicha variable ya que no hemos ejecutado ninguna instrucción del programa (obsérvese que la línea azul está situada en la primera instrucción ejecutable). Podemos empezar a ejecutar nuestro programa paso a paso pulsando la tecla "F7" (opción Siguiente Paso), veremos que la ejecución va avanzando cada vez que pulsamos la tecla, que la sentencia resaltada irá Pág. 15 Introducción a los Computadores Práctica 1 cambiando y que en la ventana MS-DOS van apareciendo los resultados de la ejecución de las instrucciones cout. También podemos observar que cuando se ejecute una instrucción cin, el depurador se detendrá esperando que introduzcamos en la ventana MS-DOS el valor esperado, una vez que pulsemos la tecla INTRO el programa proseguirá a la siguiente instrucción y el depurador volverá a su estado normal. En la lista de Variables observadas de la pestaña de depuración podremos ver los valores que van tomando las variables de nuestro programa. Sin embargo, la ejecución paso a paso de un programa con muchas instrucciones o con bucles puede ser muy larga y tediosa. También podemos ejecutar el programa de forma continua hasta una determinada instrucción en la que se detendrá, usando Puntos de Ruptura. En cada breakpoint o punto de ruptura la ejecución del programa se detendrá y podremos aprovechar para inspeccionar el valor de variables, funciones, etc. Para proseguir la ejecución usaremos la opción Saltar Paso y el programa seguirá ejecutándose hasta el próximo punto de ruptura o hasta el final del programa si no encuentra ningún breakpoint en su camino. O bien podremos reanudar la ejecución paso a paso con la opción Avanzar Paso a Paso. En la ventana MSDOS iremos viendo el resultado de la ejecución de nuestro programa, y en la lista de variables observadas como van variando los valores. NOTA: Téngase en cuenta que al tratarse de una versión beta es posible que el entorno y el depurador puedan dar algún problema y quedarse “colgado”. Estilo de codificación. Para finalizar vamos a ver unas recomendaciones básicas sobre estilo de codificación a la hora de escribir nuestros programas en C++: Un buen estilo de codificación puede suplir las deficiencias de un lenguaje de programación primitivo, mientras que un estilo pobre puede frustrar los propósitos de un excelente lenguaje. Y aunque no existen unas reglas o directrices que se puedan aplicar a todas las situaciones, si es posible definir unos principios generales para poder codificar con un buen estilo. A continuación vamos a enumerar algunas de las acciones a seguir para crear un buen estilo de codificación: • • Usar comentarios profusamente. Los comentarios situados a la derecha de las sentencias deben estar alineados en la medida de lo posible. Siempre que en un programa se ponga un bloque de comentarios como documentación interna, es deseable dejarle márgenes para así realzarlo. Estos bloques es conveniente usarlos en zonas del programa que realicen manipulaciones de datos importantes o que constituyan el "meollo" de los algoritmos utilizados. Pág. 16 Introducción a los Computadores • • • • • Práctica 1 Incluir un prólogo estándar de documentación en la cabecera del programa usando líneas de comentarios. Este prólogo deberá contener el nombre del programador, la fecha de realización del programa (o de su última modificación) y la versión del mismo con una breve historia de las modificaciones realizadas. Así mismo deberá indicarse el nombre del programa (o del fichero que lo contiene) así como una breve descripción del mismo. Elegir nombres significativos y autoexplicativos para los objetos. Usar sangrados y separadores (tabuladores, espacios y cambios de línea) para diferenciar bloques y secciones del código y aumentar la legibilidad del programa. Los sangrados deben utilizarse para destacar las sentencias anidadas en otras, como por ejemplo lo cuerpos de bucles. Los espacios para separar suficientemente las palabras y símbolos que componen las sentencias. Las líneas en blanco deben utilizarse para separar bloques de sentencias, como por ejemplo para destacar dos iteraciones consecutivas. Escribir una sola sentencia por línea. Si la sentencia es excesivamente larga se puede partir en diferentes líneas (siempre que la sintaxis del lenguaje lo permita), sangrando las sucesivas líneas para así indicar la continuación de la sentencia. En las expresiones y condiciones utilizar siempre paréntesis para separar las operaciones. Aunque muchas veces la escritura de una expresión (o condición) parezca trivial, el orden de evaluación del traductor del lenguaje puede gastarnos una mala pasada, y realizar las operaciones en un orden diferente al que esperábamos. Utilizando paréntesis cambiamos el orden de evaluación, obligando al traductor a realizar las operaciones en el orden marcado por los paréntesis. Por último vamos a enumerar algunas de las acciones que no se deben realizar en un buen estilo de codificación: • • • • No hay que ser demasiado complicado. No hay que complicarse la vida, es mejor utilizar un algoritmo sencillo aunque largo que uno corto pero complejo. Nunca partir una sentencia en varias líneas en medio de un texto literal (cadena de caracteres). No anidar de forma muy profunda. El anidamiento de decisiones y/o bucles en exceso hace perder la claridad del programa, además de ser signo de pensamiento confuso y de un diseño pobre. Como principio general, el anidamiento de las construcciones de un programa no debe exceder de tres o cuatro niveles. No emplear identificadores con propósitos múltiples. Es muy común que los programadores utilicen un mismo identificador para denotar diferentes entidades. La razón es un supuesto ahorro de almacenamiento (tres variables utilizadas una sola vez ocupan más almacenamiento que una sola variable utilizada tres veces de forma distinta). Sin embargo, esta práctica no supone tanto ahorro como se espera y en cambio puede provocar numerosos problemas, siendo deseable evitarla. Pág. 17