Capítulo 4. Estructura general de un programa. Estructuras básicas de control. Entrada y salida de datos. 1. Estructura general de un programa. Pseudocódigo y C. 1.1. Sentencia simple. 1.2. Sentencia compuesta. 2. Programación estructurada. Estructuras básicas de control. 2.1. Estructura secuencial. 2.2. Estructura alternativa. 2.3. Estructura repetitiva. 3. Entrada y salida de datos. 3.1. Entrada de datos. 3.2. Salida de datos. 1 1. Estructura general de un programa. Pseudocódigo y C. De un modo muy general se puede decir que un programa está formado por 4 partes principales: - Declaraciones: la parte donde se declaran o crean los objetos (variables, constantes, etc.) necesarios para el programa. - Entrada de datos: la parte donde se solicitan y se recogen los datos que necesita el programa para operar. - Proceso o algoritmo: la parte que realiza las operaciones sobre los datos para obtener unos resultados. - Salida de resultados: la parte que muestra o guarda los resultados obtenidos tras el proceso. En realidad, las 3 últimas partes no suelen tener una separación tan definida, mezclándose a lo largo del programa. El pseudocódigo es una notación empleada para escribir algoritmos, la cual no está sujeta a las reglas sintácticas de ningún lenguaje, por lo que su uso es rápido y sencillo. La estructura general de un algoritmo en pseudocódigo consiste simplemente en etiquetar el inicio y el final del programa, colocando entre ambas etiquetas todas las sentencias a realizar, sin necesidad de declarar las variables, constantes, etc. En C, la estructura general de un programa es la siguiente: /* Esto es una muestra de la estructura general de un programa C */ #include <libreria1.h> #include <libreria2.h> ... #define <constante1> <valor1> #define <constante2> <valor2> ... <Declaración de variables globales> // Comienzo del módulo principal void main(void) { <Declaración de variables locales> Instrucciones: entrada, proceso y salida; } // Fin del programa Los comentarios que se deseen incluir en un programa en C se inician con /* y se finalizan con */, pudiendo englobar varias líneas. También a continuación de // se puede incluir una sola línea de comentario. Toda sentencia que comience por # se denomina directiva de preprocesador. Es una de las características del C. Con la directiva #include <libreria.h> se indica al compilador que recupere el código del archivo predefinido libreria.h. Los archivos cabecera libreria.h son vulgarmente llamados “includes”. Estos archivos contienen identificadores, constantes, 2 macros, prototipos de funciones, etc. permitiendo tener las declaraciones fuera del programa principal. Da más modularidad al programa. Todo programa en C debe tener una función main(). Es la función principal y establece el punto en el que comienza la ejecución. 1.1. Sentencia simple. Es una línea que contiene una expresión válida en C acabada en punto y coma “;”. Ej. Calcula_minimo(x, y); A = b * c; //Llamada a una función //Sentencia de asignación 1.2. Sentencia compuesta. Una sentencia compuesta es un grupo de sentencias que se trata como si fuera una única sentencia. En C, las sentencias compuestas comienzan con una llave { y terminan con la llave complementaria }. Generalmente las sentencias compuestas se usan para indicar el conjunto de operaciones a realizar cuando se cumplen ciertas condiciones (estructuras alternativas IF-ELSE), o bien el conjunto de operaciones que se quieren repetir hasta que se cumpla una condición (estructuras repetitivas FOR, WHILE, ...). Dentro de una sentencia compuesta, al principio, pueden crearse variables, que serán locales para ese bloque delimitado por { y }. 2. Programación estructurada. Estructuras básicas de control. Como ya se ha mencionado, la programación estructurada está basado en el uso de solamente de las tres estructuras básicas de control: secuencial, alternativa y repetitiva. Veamos cada una de ellas. 2.1. Estructura secuencial. La estructura de control secuencial simplemente dice que las sentencias de un programa se ejecutan en el orden en que están escritas. 2.2. Estructura alternativa. La estructura de control alternativa será desarrollada en un capítulo posterior. 2.3. Estructura repetitiva. La estructura de control alternativa será desarrollada en un capítulo posterior. 3. Entrada y salida de datos. Las instrucciones de entrada y salida de datos se usan para poder comunicarse con el programa. 3 3.1. Entrada de datos. Mediante las instrucciones de entrada se puede guardar en una variable del programa datos que proceden de un dispositivo externo, como un teclado, un disco duro, etc. En pseudocódigo la instrucción de entrada desde el teclado será: Leer <Nombre_Variable> Cuando el procesador ejecuta esa sentencia, se realiza una pausa en la ejecución del programa, esperando a que se teclee un dato y se pulse <ENTER>. El dato tecleado será guardado en la variable <Nombre_Variable>. En C existen varias formas de realizar entradas desde teclado: - Funciones para entrada de un carácter: a) int getchar(void) Está en la librería stdio.h. Esta función tiene un problema, ya que fue definida para ser compatible con la versión original de C basada en UNIX: guarda los caracteres que se tecleen en el buffer de entrada, hasta que se pulse <ENTER>. Después recoge sólo el primer carácter y lo almacena en la variable especificada. Esto hace que puedan quedar uno o más caracteres en el buffer de entrada. Así, las posteriores funciones de entrada en el programa leerán esos caracteres del buffer, obteniéndose resultados inesperados. Ej. char letra; letra = getchar(); En caso de error devuelve EOF, constante definida en stdio.h con valor igual a –1. b) int getche(void) Está en la librería conio.h. El carácter tecleado será almacenado y visualizado en pantalla (tiene eco). La ejecución del programa continuará sin necesidad de pulsar <ENTER>. Ej. char letra; letra = getche(); c) int getch(void) Está en la librería conio.h. Funciona exactamente como la anterior, excepto que el carácter tecleado no será visualizado en pantalla (sin eco). - Función de entrada de cadenas, gets: está en la librería stdio.h. 4 char * gets(char * cadena) Recoge una cadena de caracteres introducida por teclado hasta pulsar <ENTER> y la guarda en la dirección apuntada por su argumento (cadena). El salto de carro no se introduce en la cadena, se sustituye por el carácter nulo ‘\0’. En C, todos los nombres de cadenas son punteros al primer carácter de la cadena. Una cadena es una sucesión de caracteres. Se declara de la forma: char cadena[nºcaracteres]; Ej. char cadena[25]; gets(cadena); La función gets no controla automáticamente la longitud de la cadena, es decir que si se teclean más caracteres que la longitud de la cadena, pueden ocurrir errores inesperados. - Función de entrada con formato, scanf: está en la librería stdio.h. int scanf( char * cadena_control, lista_de_argumentos) En caso de error devuelve EOF. La lista_de_argumentos está formada por las direcciones de memoria (punteros) de las variables donde se quiere guardar los valores tecleados. Por tanto, debe usarse el operador & delante del nombre de cada variable, excepto para cadenas (ya son punteros). La cadena_control se escribe entre comillas dobles y está formada por: Especificadores de formato Caracteres de espacios en blanco Caracteres de no-espacios en blanco Cada especificador de formato se corresponderá con una variable de la lista de argumentos, según el orden en que estén escritos. Por lo tanto, debe haber el mismo número de especificadores de formato que argumentos en la lista. Los especificadores de formato tienen la forma: %[nº_de_caracteres]type El valor de type puede ser: Código c d Significado Lee un único carácter Lee un entero 5 i ld e f lf Lf g o s x p n u [...] Lee un entero Lee un entero de tipo long Lee un número en coma flotante Lee un número en coma flotante Lee un número en coma flotante double Lee un número en coma flotante long double Lee un número en coma flotante Lee un número octal Lee una cadena Lee un número hexadecimal Lee un puntero Recibe un valor entero igual al número de caracteres leídos Lee un entero sin signo Muestrea un conjunto de caracteres (cadenas) El nº de caracteres será un número entero opcional que se coloca entre % y type. Ese número indica el número máximo de caracteres a almacenar en la variable correspondiente, de forma que el exceso no se tendrá en cuenta. Ej. int edad; scanf( "%3d",&edad);//Después de teclear 3 números, //lo que se teclee no se guarda. int i,j; scanf( "%o%x",&i,&j); //Uno en octal y otro en hexade. char a,b,c; scanf( "%c%c%c", &a,&b,&c); // Lee tres letras. El type [...] se usa para cadenas de caracteres e indicará que sólo serán guardados los caracteres tecleados que coincidan con los especificados entre los corchetes. Cuando se teclee un carácter que no esté entre los corchetes, no se almacenarán más caracteres en la variable correspondiente. Ej. char cad[3]; scanf("%[ABCD]",cad); //cad no lleva &, ya que el //nombre de una cadena es una dirección. Si se teclea A AB aB DD BH EA Se asigna a cad CAD = "A" CAD = "AB" CAD = "" CAD = "DD" CAD = "B" CAD = "" 6 El type [^....] se usa para cadenas de caracteres e indicará que sólo serán almacenados los caracteres tecleados que no coincidan con los especificados dentro de los corchetes. La función scanf deja de guardar en la variable los caracteres tecleados cuando se pulsa espacio en blanco, \t (tabulador) o \n (retorno de carro o <ENTER>), lo cual causa problemas en el caso de teclear cadenas de caracteres que incluyan espacios en blanco. Ej. char nombre[20]; scanf ("%s",nombre); //Si se teclea: “Fco. Javier” se guarda en la //variable nombre = "Fco.", el esp.blanco corta. Ese problema se puede solucionar con el type [^\n], es decir almacenar todos los caracteres tecleados, excepto el retorno de carro. Ej. scanf ("%[^\n]",nombre); //Guarda nombre = "Fco. Javier" También scanf deja de almacenar los caracteres tecleados cuando no se cumpla el type especificado, con lo que pueden quedar caracteres en el buffer de entrada, de manera que la siguiente instrucción de entrada recogería esos caracteres del buffer. Ej. int i; scanf("%d", &i); //Si se teclea "A12", en i queda i=0, ya que %d //espera un nºentero y A no lo cumple; lo que se //teclee después de la A (el 12) ya no se guarda en i. Para eliminar los caracteres que pueden quedar en el buffer de teclado, siempre debe usarse la función fflush(stdin) después de cada scanf. Ej. int num; char car; scanf("%d", &num); fflush(stdin); scanf("%c", &car); fflush(stdin); Como decíamos antes, en la cadena de control, además de los especificadores de formato, pueden colocarse caracteres de espacios en 7 blanco y caracteres de no-espacios en blanco, que se usan cuando un scanf incluye varios especificadores de formato. Un carácter de espacio en blanco antes o después del especificador de formato hace que scanf lea los caracteres blancos pero no los almacena. Esta es la opción por defecto, no es necesario especificarla. Un carácter de espacio en blanco puede ser un espacio en blanco, un tabulador (\t) o un retorno de carro (\n). Ej. int edad; double salario; scanf("%d\n%lf",&edad,&salario); //Al teclear un blanco, un tabulador o <enter> se deja //de guardar en edad para guardar en salario. Un carácter de no-espacio en blanco es cualquier carácter que no sea blanco, \t o \n. Si se coloca una serie de caracteres no-espacios blanco en la cadena de control, scanf obliga a que se tecleen esos caracteres pero no los almacena. Si no se tecleean esos caracteres en la posición indicada, no se guarda lo tecleado en la variable. Ej. int i; double j; scanf("$%d", &i); //Si se teclea $74 se asigna i=74 //Si se teclea 74 se asigna i=0 scanf("%d:%lf",&i,&j); //Al pulsar ':' se deja de //asignar a i, y se comienza a asignar a j. 3.2. Salida de datos. Mediante las instrucciones de salida se puede enviar a un dispositivo externo (monitor, disco duro, etc.) los datos que están en las variables de un programa. En pseudocódigo la instrucción de salida al monitor (visualizar) será: Escribir <Nombre_Variable> Cuando el procesador ejecuta esa sentencia, se visualiza en pantalla el valor almacenado en la variable <Nombre_Variable>. En C existen varias formas de realizar salidas a monitor: - Funciones para salida de un carácter: está en la librería stdio.h. 8 int putchar(int c) El entero c es convertido a carácter y se envía al monitor. En caso de error devuelve EOF. Ej. char car; car = getchar(); putchar(car); - Función de salida de cadenas, puts: está en la librería stdio.h. int * puts(char * cadena) Visualiza en el monitor el contenido de cadena, realizando a continuación un salto de línea. En caso de error devuelve EOF, si no devuelve un valor distinto de cero. Reconoce las mismas secuencias de control (secuencias de backslash) que la función printf, que veremos más adelante. La función puts es más rápida que printf, ya que tiene menos código. Ej. char cadena[25]; gets(cadena); puts(cadena); - Función de salida con formato, printf: está en la librería stdio.h. int printf(char * cadena_control, lista_de_argumentos) Esta función puede visualizar varios datos por pantalla. Devuelve un valor entero que coincide con el número de caracteres visualizados. En caso de error, devuelve un valor negativo. La cadena_control se escribe entre comillas dobles y está formada por especificadores de formato y por los caracteres que se desee visualizar. Los especificadores de formato tienen la siguiente forma: %[flags] [ancho de campo] [precisión] type Como se ve, cada especificador de formato comienza por %; este carácter y type son obligatorios, el resto son opcionales (entre corchetes). La lista_de_argumentos del prototipo de printf está formada por las variables o constantes que se quiera visualizar. Cada argumento de la lista debe tener su especificador de formato. El modificador type del especificador de formato puede ser: 9 Código c d i ld e E f lf Lf g G o s u x X p n %% Formato Carácter Enteros con signo Enteros con signo Enteros con signo long Coma flotante, notación científica (e minúscula) Coma flotante, notación científica (E mayúscula) Coma flotante float Coma flotante double Coma flotante long double Usa e o f, el más corto Usa E o f, el más corto Octal sin signo Cadena de caracteres Enteros decimales sin signo Hexadecimales sin signo (letras minúsculas:a - f) Hexadecimales sin signo (letras mayúsculas:A –F) Mostrar un puntero El argumento asociado será un puntero a entero al que se le asigna el número de caracteres escritos Visualizar el signo % Ej. char car; float num; car = getch(); num = 12.25; printf("Carácter: %c Número: %f", car, num); Si se visualiza una variable con un tipo (type en el printf) que no se corresponde con el que se ha declarado, el C realiza la conversión de forma automática, según se ha visto anteriormente. Veamos un ejemplo para el caso de la conversión de caracteres a números. Ej. char car = ‘Ñ’; unsigned char uncar = ‘Ñ’; printf(“%c”, car); //Visualiza Ñ. printf(“%c”, uncar); //Visualiza Ñ. printf(“%d”, car); //Visualiza –91, ya que se tiene //en cuenta el bit de signo al convertir a decimal. printf(“%d”, uncar); //Visualiza 165, ya que no se //tiene en cuenta el bit de signo en la conversión. Los especificadores %e y %E, que se usan para coma flotante, muestran su argumento en notación científica, que es: 10 %e %E -> -> x.dddddde +/- yy x.ddddddE +/- yy Con %g da la salida más corta entre %f y %e; con %G da la salida más corta entre %f y %E. Ej. double d = 100.0; printf( "%g",d); d = 100000000.0; printf( "%g",d); //Visualiza con formato %f //Visualiza con formato %e; es más corto que %f. Con %p se muestra una dirección de memoria, en un formato compatible con el tipo de direccionamiento utilizado por la computadora. Ej. int i; printf( "La dirección de esa variable es %p", &i); El especificador %n guarda en el argumento asociado el número de caracteres impresos hasta el momento de encontrarse el %n; el argumento será un puntero a una variable (&<Variable>). Ej. int imp; printf( "Contará los caracteres hasta aquí%n y luego" "lo comprobamos.", &imp); printf( "Se han impreso %d caracteres.", imp); Dentro de un especificador de formato, hemos visto que se puede poner la anchura del campo, como un número entero que se sitúa entre el % y el type: %[flags] [ancho de campo] [precisión] type Ese número entero especificará la longitud mínima que ocupará el dato al visualizarse, teniendo en cuenta el punto decimal y los decimales. Ej. int i = 100; printf("i = %6d", i); // Sale i = bbb100; 3 blancos // ya que la longitud mínima es 6. Si la longitud del dato es mayor que la especificada en el formato, se 11 visualiza todo el dato, no se tiene en cuenta esa longitud mínima. Si en lugar de rellenar con blancos, se desea rellenar con ceros, se coloca un 0 entre el % y el entero que indica la longitud: Ej. int i = 100; printf( "i = %05d", i); // Sale i = 00100; Dentro de un especificador de formato, se puede poner la precisión: %[flags] [ancho de campo] [precisión] type Se coloca entre el % y el type, después del ancho del campo si existe, con la forma .número, o sea un punto seguido de un número entero. Ese entero indica la precisión, es decir el número de decimales a visualizar, completando con ceros por la derecha si es necesario, o truncando los decimales si no caben en la precisión indicada (redondeando al valor más cercano). Ej. float num = 100.18; printf("Número = %f", num); //Sale: f = 100.180000 printf("Número = %7.1f",num); //Sale: f = bbbb100.2 printf("Número = %.3f", num); //Sale: f = 100.180 Si se usa con cadenas (%s), la precisión indicará la longitud máxima que se visualizará; la longitud mínima viene dada por el ancho de campo indicado. “%8.10s” Escribe la cadena de 8 a 10 caracteres. Si no se especifica la precisión se toma una por defecto que es: - para tipos f, e y E son 6 dígitos decimales. - para tipos g y G son todos los dígitos significativos. Si se pone precisión .0 se redondea al entero más cercano. Veamos las banderas o flags: %[flags] [ancho de campo] [precisión] type Flag + b Significado Justifica a la izquierda, inserta blancos a dcha. Visualiza el signo +/Un blanco hace que sólo visualice el signo negativo, y en lugar del positivo sale un blanco 12 Ej. printf ("|%-15s|", "hola"); //Sale |holabbbbbbbbbbb| printf ("|%15s|", "hola"); //Sale |bbbbbbbbbbbhola| Existen las secuencias de backslash (o secuencias de escape) para realizar operaciones de control que pueden ser ejecutadas con printf como otros caracteres. Estas secuencias son: Secuencia \a \b \f \n \r \t \v \\ \' \" \0 \ddd \xdd Significado Sonido Espacio atrás (backspace) Salto de página (sólo impresora) Salto de línea Retorno de carro Tabulación horizontal Tabulación vertical (sólo impresoras) Barra invertida Comilla simple Comilla doble Nulo Tres dígitos que son la rep.octal del carácter ASCII a visualizar. Una 'x' más dos dígitos que son la rep.hexadec. del carácter ASCII. 13 EJERCICIOS - CAPITULO 4: Realizar los siguientes algoritmos en pseudocódigo y en C. 1. Introducir 2 números reales y visualizar la suma, la resta, el producto, la división y el resto de la división entera. Sólo deben visualizarse 2 decimales. 2. Introducir nombre, edad, sueldo y teléfono para 5 personas. Después deben visualizarse todos los datos tecleados, sacando una persona en cada fila. El sueldo tendrá 2 decimales. 3. Introducir un capital y un porcentaje de interés tres veces. Visualizar a continuación los 3 capitales, sus 3 porcentajes de interés, el interés que produce cada capital y la suma de cada capital con su interés producido. Al final debe visualizarse también el total de las tres sumas calculadas. Todas las cantidades tendrán 2 decimales. Notas: La función gotoxy(columna, fila) coloca el cursor en la columna (de 1 a 80, izquierda a derecha) y fila (de 1 a 25, arriba a abajo) indicada de la pantalla. La función clrscr() borra la pantalla completa. 14