INTRODUCCIÓN A LA PROGRAMACIÓN 1 PRÁCTICA 3: FUNCIONES Y PROCEDIMIENTOS 1ª PARTE: FUNCIONES TRANSFORMACIÓN DE LEA A C LEA C DEFINICIÓN DE UNA FUNCIÓN func nombre (e1:T1; e2:T2; ...; en:Tn) dev (r:Tr) Pre: {P(e1, e2, ..., en)} Post: {Q(e1, e2, ..., en, r)} cons definición de constantes var declaración de variables prin sentencias fin Tr nombre (T1 e1, T2 e2, ..., Tn en) { Tr r; declaración de variables func factorial (n: entero) dev (f: entero) Pre: {n≥0} Post: {f=n!} var i: entero prin <f,i> := <1,0> mientras i < n i := i+1 f := f*i fmientras fin int factorial (int n) { int f, i; instrucciones return r; } f = 1; i = 0; while (i < n) { i++; f = f*i; } return f; } LLAMADA A UNA FUNCIÓN En una asignación: variable := nombre (lista de parámetros) En una asignación: variable = nombre (lista de parámetros); Dentro de una expresión: E (x, nombre(lista de parámetros)) Dentro de una expresión: E (x, nombre(lista de parámetros)) a, b, f, año, ndias: entero int a, b, f, anyo, ndias; f := factorial (5) f := factorial (a) / factorial (a-b) f = factorial (5); f = factorial (a) / factorial (a-b); si esBisiesto(año) ndias := 366 fsi if (esBisiesto(anyo)) ndias = 366; Práctica 3: Funciones y Procedimientos 2 EXPERIMENTOS E.3.1 Escriba el siguiente programa, compílelo y ejecútelo. ¿Por qué no funciona correctamente? Corríjalo. #include <stdio.h> float max_doble(float,float); void main(void) { float x,y; printf("\nDeme dos números separados por un blanco: "); scanf("%f%f",&x,&y); printf("El doble-máximo de %f y %f es %f\n",x,y,max_doble(x,y)); } float max_doble(float a, float b) { float max; if (a>b) max=a; else max=b; max=max*2; } E.3.2 El siguiente programa da tres “warning”, de los cuales uno de ellos asegura que la función cuadrado está "redeclarada". ¿Cuál es el problema? Corríjalo. #include <stdio.h> void main(void) { float x; printf("\nDeme un número: "); scanf("%f",&x); printf("El cuadrado de %f es %f\n",x,cuadrado(x)); } float cuadrado(float y) { float z; z=y*y; return(z); } EJERCICIOS DE SINTAXIS PROPUESTOS S.3.1 El siguiente programa indica si un número leído desde la entrada estándar es par: #include <stdio.h> void main (void) { int numero; scanf ("%d", &numero); if (numero%2 == 0) printf ("Es un número par"); else printf ("Es un número impar"); } Convierta el programa en una función que reciba como parámetro el número y devuelva como resultado un valor de tipo lógico que indique si el número es o no par. S.3.2 Modifique la función anterior para que sirva para comprobar si el número es múltiplo de un número variable n, que será otro parámetro de entrada de la función. Práctica 3: Funciones y Procedimientos S.3.3 3 El siguiente programa calcula el factorial de un número: #include <stdio.h> void main (void) { int n, i; long factorial; printf ("\nIntroduzca un número entero positivo: "); scanf ("%d", &n); factorial = 1; for (i=1; i<=n; i++) factorial = factorial * i; printf ("\nEl factorial de %d es: %ld",n, factorial); } Modifíquelo transformándolo en una función que reciba el número como parámetro de entrada y devuelva el resultado como salida. Utilice el código que se proporciona a continuación. #include <stdio.h> void main (void) { int n; printf ("\nIntroduzca un número entero positivo: "); scanf ("%d", &n); printf ("\nEl factorial de %d es: %ld",n, factorial(n)); } S.3.4 Utilizando la función creada en el apartado anterior, escriba una función que calcule el número combinatorio dado por la fórmula siguiente: ⎛ n⎞ n! ⎜⎜ ⎟⎟ = ⎝ m ⎠ m! (n - m)! donde debe cumplirse que n > m. El prototipo de la función será int numero_combinatorio (int n, int m); S.3.5 El siguiente programa calcula la potencia n-ésima xn de un número entero x. #include <stdio.h> void main (void) { int n, i; double x, potencia; printf ("\nIntroduzca la base y el exponente: "); scanf ("%lf%d", &x, &n); potencia = 1; for (i=0; i<n; i++) potencia = potencia * x; printf ("\nLa potencia es %lf", potencia); } Modifíquelo transformándolo en una función que reciba los valores de x y n comos parámetro de entrada y devuelva el resultado como salida. S.3.6 El siguiente programa lee dos números enteros de la entrada estándar y calcula e imprime el mayor de ellos: #include <stdio.h> void main (void) { int a, b, maximo; scanf ("%d%d", &a, &b); if (a> b) maximo = a; Práctica 3: Funciones y Procedimientos 4 else maximo = b; printf ("\nEl máximo de %d y %d es %d", a, b, maximo); } Convierta el programa en una función que reciba ambos números como parámetros de entrada y devuelva el mayor de ellos como resultado. EJERCICIOS DE ALGORITMICA PROPUESTOS A.3.1 Realizar una función de nombre Siguiente tal que, recibiendo un número primo mayor que uno, devuelva el número primo inmediatamente siguiente y superior a dicho número primo. Por ejemplo, si se invoca Siguiente(7), la función devolverá el número 11. A.3.2 Sea el siguiente programa incompleto: #include <stdio.h> void main (void) { int x1, x2, suma; printf ("\nIntroduce dos valores: "); scanf ("%d%d", &x1, &x2); suma = suma_intervalo (x1, x2); printf ("\nLa suma de los valores del intervalo es: %d", suma); } Complete el programa escribiendo la función suma_intervalo que calcula la suma de todos los números enteros comprendidos entre los dos parámetros de entrada, ambos inclusive. A.3.3 Escriba una función que reciba como parámetro de entrada el valor de un año y devuelva como resultado un valor de tipo lógico que indique si es o no un año bisiesto. Recuerde, una vez más, la regla: "Un año es bisiesto si es divisible por 400, o bien si es divisible por 4 pero no por 100" A.3.4 Escriba una función que reciba como parámetros de entrada dos fechas dadas por un día, mes y año y devuelva como resultado el número de días transcurridos entre ambas. Tenga en cuenta los posibles años bisiestos que puedan existir entre ellas (utilice la función realizada en el ejercicio anterior). A.3.5 Desarrolle una función que reciba como parámetros las dos coordenadas cartesianas (x, y) de un punto del plano y devuelva como resultado un número del 1 al 4 que indique el cuadrante al cual pertenece al punto (no considere los ejes de coordenadas). A.3.6 Escriba una función que reciba como parámetro de entrada un número entero y devuelva como resultado el número de cifras del número. Para ello, considere lo siguiente: dado un número cualquiera, como por ejemplo el 173, se tiene que 173 / 10 = 17 (todas las cifras salvo la última) Habrá que repetir pues este proceso hasta que el número resultante de la división tenga una sola cifra. A.3.7 Escriba una función que reciba como parámetros de entrada un valor entero y compruebe si se encuentra comprendido entre dos valores constantes OPMIN y OPMAX. La función devolverá un valor lógico cierto en caso afirmativo, y falso en caso contrario. A.3.8 Modifique la función anterior para que los dos límites del intervalo sean sendos parámetros de la función. Escriba varias posibles llamadas a la función, e indique el valor devuelto en cada caso. A.3.9 Escriba una función que reciba como parámetros de entrada dos instantes de tiempo expresados en horas, minutos y segundos y devuelva como resultado un 1 o un 2 según el primer instante de tiempo sea anterior al segundo o Práctica 3: Funciones y Procedimientos 5 viceversa. A.3.10 Escriba una función que reciba como parámetros de entrada tres números enteros que representan las longitudes de tres segmentos rectilíneos, y devuelva como resultado un valor de tipo lógico que indique si dichos segmentos pueden formar o no un triángulo (la condición necesaria pero no suficiente es que ninguno de los segmentos tenga una longitud superior a la suma de los otros dos). A.3.11 Desarrolle una función que presente en la salida estándar los múltiplos de n menores que m. Tanto n como m serán dos parámetros de entrada de la función. A.3.12 Construya una función que imprima la suma de los cuadrados de los n primeros números naturales, siendo n un valor recibido como parámetro. A.3.13 Recuerde el programa que calcula de forma aproximada el seno de un ángulo comprendido entre -π/2 y π/2 radianes, según la fórmula 3 angulo angulo seno = angulo + 6 120 5 propuesto en la práctica 4. Transforme este programa escribiendo las siguientes funciones: · · · · lee_datos lee el valor del ángulo, asegurándose de que se encuentra en el rango adecuado. calcula_seno calcula el seno del ángulo, aplicando la fórmula aproximada. presenta_seno presenta en la salida estándar los resultados obtenidos con un formato adecuado. potencia calcula la potencia de un número real elevado a un exponente entero positivo. El diagrama de bloques del nuevo programa es el siguiente: programa lee_datos calcula_seno presenta_seno potencia A.3.14 Recuerde el programa de cálculo de superficies del tema anterior. Transforme este programa escribiendo las siguientes funciones: · · · lee_opcion lee la opción deseada y comprueba su validez (repase los ejercicios anteriores). menú muestra el menú en la pantalla. cuadrado, circulo, rectángulo, trapecio, triángulo calculan la superficie correspondiente. ========================================== CÁLCULO DE SUPERFICIES (versión 3.0) ========================================== 1. Cuadrado lado*lado 2. Círculo pi*radio*radio 3. Rectángulo base*altura 4. Trapecio (base1+base2)*altura/2 5. Triángulo (base*altura)/2 0. Salir del programa ========================================== El diagrama de bloques del programa es el siguiente: Práctica 3: Funciones y Procedimientos 6 programa lee_opción menú cuadrado círculo rectángulo trapecio triángulo A.3.15 Escriba un programa que permita llevar los gastos e ingresos menores de un millón de pesetas de una empresa. El programa recogerá estos datos desde la entrada estándar y presentará un menú como el siguiente: ================================= CONTROL DE GASTOS (versión 1.0) ================================= 1. Ingresos 2. Gastos 3. Salida ================================= El diagrama de bloques es el siguiente: programa menú lee_opción ingresos gastos salida Donde las funciones realizan las siguientes operaciones: · · · · · menú muestra el menú en la pantalla. lee_opcion lee la opción del teclado. ingresos lee nuevos ingresos y los suma al saldo actual. gastos lee nuevos gastos y los resta del saldo actual (que nunca podrá ser negativo). salida muestra el saldo actual (ingresos - gastos). A.3.16 Escriba un programa realice la siguiente operación: 1. 2. 3. 4. generará un número aleatorio de cuatro cifras. leerá desde la entrada estándar un número de cuatro cifras diferentes. comparará el número leído con el número generado por la máquina Mostrará en la pantalla un número que indicará cuántas cifras del número leído existen y están colocadas en su posición correcta en el número generado, y cuántas cifras existen pero no están colocadas en su posición correcta. El programa repetirá los pasos 2 a 4 hasta que el número de cifras en posición correcta sea 4. Sea el siguiente ejemplo de ejecución (para el número generado 5234) Introduza Cifras Cifras Introduza Cifras Cifras ... su en en su en en número: 2754 su posición: otra posición: número: 1534 su posición: otra posición: 1 2 2 1 A.3.17 Realice un programa que lea de la entrada estándar un número de hasta 8 cifras y muestre por la salida estándar la cifra resultante de aplicar el siguiente proceso: Práctica 3: Funciones y Procedimientos a) b) 7 Sumar el valor absoluto de todas las cifras del número. Si el valor resultante tiene más de una cifra, volver a sumar todas sus cifras sucesivamente hasta obtener un valor de una única cifra. Ejemplos: Valor introducido por el usuario: 68543210 68543210 -> 6+8+5+4+3+2+1+0 = 29 29 -> 2+9 = 11 11 -> 1+1 = 2 Valor a mostrar: 2 Valor introducido por el usuario: 10000 10000 -> 1+0+0+0+0 = 1 Valor a mostrar: 1 Práctica 3: Funciones y Procedimientos 8 2ª PARTE: PROCEDIMIENTOS TRANSFORMACIÓN DE LEA A C LEA C DEFINICIÓN DE UN PROCEDIMIENTO proc nombre (ent e1:Te1; e2:Te2; ...; en:Ten; sal s1:Ts1; s2:Ts2; ...; sm:Tsm; ent/sal r1:Tr1; r2: Tr2; ...; rp:Trp) Pre: {P(e1, ..., en, r1, ..., rp)} Post: {Q(e1, ..., en, s1, ..., sm, r1, ..., rp)} cons definición de constantes var declaración de variables prin sentencias exp (ei, si, ri, x) sentencias fin #include <assert.h> typedef Ts1 * PTs1; ... typedef Tsm * PTsm; typedef Tr1 * PTr1; ... typedef Trp * PTrp; void nombre (Te1 e1, Te2 e2, ..., Ten en, PTs1 s1, PTs2 s2, ..., PTsm sm, PTr1 r1, PTr2 r2, ..., PTrp rp) { declaración de variables assert (P(e1, ..., en, r1, ..., rp)); instrucciones exp (ei, *si, *ri, x); instrucciones } proc multDiv (ent op1:entero; op2:entero; ent/sal mult:entero; div:entero) Pre: { op2 ≠ 0 } Post: { mult = op1 * op2 ∧ div = op1 / op2 } Prin mult := op1 * op2 div := op1 / op2 fin typedef int *Pint; void multDiv (int op1, int op2, Pint mult, Pint div) { assert (op2 != 0); *mult = op1 * op2; *div = op1 / op2; } Nota: distinguir entre * de multiplicación y * como operador unario para representar el contenido. LLAMADA A UN PROCEDIMIENTO nombre (lista de parámetros) nombre (lista de parámetros); operando1, operando2, producto, cociente: entero int operando1, operando2, producto, cociente; multDiv (5, 9, producto, cociente) multDiv (operando1, operando2, producto, cociente) multDiv (5, 9, &producto, &cociente); multDiv (operando1, operando2, &producto, &cociente); Práctica 3: Funciones y Procedimientos 9 EXPERIMENTOS E.3.3 La función intercambia pretende intercambiar los valores de sus dos parámetros. #include <stdio.h> void intercambia (int, int); void main (void) { int a=3, b=5; printf ("\na vale %d y b vale %d", a, b); intercambia (a, b); printf ("\na vale %d y b vale %d", a, b); } void intercambia (int x, int y) { int temp; temp = x; x = y; y = temp; } ¿Realiza la función su cometido? En caso contrario modifíquela adecuadamente para que lo haga. E.3.4 El siguiente programa debe mostrar en pantalla el mensaje El código es 12 y el tipo es B Complete los huecos del programa para que funcione correctamente. #include <stdio.h> typedef int * Pint; typedef char * Pchar; void inicializa (Pint, Pchar); void main (void) { int codigo; char tipo; inicializa ( ______ codigo, ______ tipo); printf ("\nEl código es %d y el tipo es %c", codigo, tipo); } void inicializa (______ c, ______ t) { ______ c = 12; ______ t = 'B'; } E.3.5 Al compilar el siguiente programa el compilador nos informa de unos errores. ¿Qué indican estos errores? Modifique el código para que funcione correctamente. #include <stdio.h> #define PI 3.1416 typedef double * Pdouble; void areaLong(double, Pdouble, Pdouble); void main(void) { double r,a,l; printf("\nIntroduzca el rádio de la circunferencia: "); scanf("%lf",&r); areaLong(r,&a,&l); printf("El área es %lf, y la longitud es %lf.\n",a,l); } void areaLong(double radio, Pdouble area, Pdouble longi) { Práctica 3: Funciones y Procedimientos 10 area = PI*radio*radio; longi = 2*PI*radio; } EJERCICIOS DE SINTAXIS PROPUESTOS S.3.7 Una función de nombre logaritmo recibe como parámetros un número real de doble precisión y un entero que representa la base, y calcula el logaritmo de dicho número en dicha base. Escriba los prototipos en lenguaje C de dicha función en los dos casos siguientes: a) b) La función devuelve el valor del logaritmo obtenido. El logaritmo es un parámetro de salida de la función. Realice una llamada para cada prototipo, declarando las variables adecuadas. S.3.8 Escriba una función void maxmin (int x1, int x2, Pint max, Pint min); que reciba como parámetros de entrada dos números enteros x1 y x2 y devuelva a través de los parámetros de salida max y min el máximo y el mínimo, respectivamente, de ambos números. S.3.9 Modifique la función anterior para que calcule también el valor medio de ambos números. El nuevo prototipo de la función será ahora el siguiente: void maxmin (int x1, int x2, Pint max, Pint min, Pint media); S.3.10 Realice una función que reciba como parámetros dos números enteros y los devuelva ordenados conteniendo el primer parámetro el mayor de los dos números y el segundo el menor de ellos. El prototipo de la función es el siguiente: void ordena_dos_numeros (Pint mayor, Pint menor); S.3.11 La función int valida_dato (int dato, int min, int max, int defecto) { int resul=defecto; if (dato >= min && dato <= max) resul = dato; return (resul); } comprueba si el parámetro de entrada dato se encuentra comprendido entre los dos límites min y max. En caso afirmativo, devuelve el propio valor dato como resultado, mientras que en caso negativo devuelve el valor del parámetro defecto. Modifique esta función para que adopte el siguiente prototipo: void valida_dato (Pint dato, int min, int max, int defecto); es decir, que dato pase a ser un parámetro de entrada y salida. S.3.12 La función Logico fecha_valida (int dia, int mes, int anyo); recibe como entrada una fecha (día, mes y año) y devuelve Cierto si la fecha es válida y Falso si no lo es. Modifique el prototipo de esta función para que el valor devuelto pase a ser un parámetro de salida. S.3.13 Sea la siguiente función Práctica 3: Funciones y Procedimientos 11 void feliz_navidad (int n) { int i, j; i = 1; while (i <= n) { for (j=i; j<n; j++) putchar (' '); for (j=1; j<=2*i-1; j++) putchar ('*'); i++; printf ("\n"); } for (i=1; i<n; i++) putchar (' '); printf ("#\n"); } Siga la traza de la ejecución de la misma cuando se invoca con la llamada feliz_navidad (5); Hágalo primero a mano y compruébelo después ejecutándola en el ordenador. EJERCICIOS DE ALGORÍTMICA A.3.18 El siguiente programa incompleto imprime todas las tablas de multiplicar del 1 al 10. #include <stdio.h> void main (void) { int i; for (i=1; i<=10; i++) { printf ("\nTabla de multiplicar del %d:\n", i); tabla_multiplicar (i); } } Complete el programa escribiendo la función tabla_multiplicar que imprime la tabla de multiplicar del número que recibe como parámetro. A.3.19 Dadas las definiciones de tipo typedef int * Pint; typedef enum {sumar, restar} Modo; escriba una función void incrementa (Modo m, int cantidad, Pint numero); que sume o reste el valor de la variable cantidad a la variable numero en función de que la variable m tome el valor sumar o restar, respectivamente. Por ejemplo, si cantidad=3 y numero=5, tras la llamada incrementa (sumar, cantidad, &numero); el valor de la variable numero será 5+3=8, mientras que tras la llamada incrementa (restar, cantidad, &numero); el valor de la variable numero será 5-3=2. Práctica 3: Funciones y Procedimientos 12 El programa principal es: void main (void) { int cant, num; scanf(“%d%d”, &cant, &num); printf ("\n1. El valor del número es: %d", num); incrementa (sumar, cant, &num); printf ("\n2. El valor del número es: %d", num); incrementa (restar, cant, &num); printf ("\n3. El valor del número es: %d", num); } A.3.20 Realice una función que reciba como parámetros tres números enteros y los devuelva ordenados conteniendo el primer parámetro el mayor de los tres números, el segundo el valor intermedio y el tercero el menor. El prototipo de la función es el siguiente: void ordena_tres_numeros (Pint mayor, Pint medio, Pint menor); A.3.21 Escriba una función Logico fecha_valida (int dia, int mes, int anyo); que reciba como entrada una fecha (día, mes y año) y devuelva Cierto si la fecha es válida y Falso si no lo es. Para que una fecha sea válida el valor del mes debe estar comprendido entre 1 y 12 y el valor del día debe estar de acuerdo con el mes (en el caso del mes de febrero, debe tenerse en cuenta además si el año es bisiesto o no). Reescriba la función para que el valor devuelto pase a ser un parámetro de salida (véase ejercicio S.6.6). A.3.22 Realice una función void siguiente_fecha (Pint dia, Pint mes, Pint anyo); que reciba a través de los parámetros dia, mes y anyo una fecha válida y devuelva a través de los mismos la siguiente fecha válida del calendario. Por ejemplo, Entrada 31 1 28 2 31 12 2 1 1996 1996 1996 1997 Salida 1 2 29 2 1 1 3 1 1996 1996 1997 1997 A.3.23 Realice una función void siguiente_instante (Pint seg, Pint min, Pint hor); que reciba a través de los parámetros seg, min y hor un instante de tiempo válido y devuelva a través de los mismos el siguiente instante de tiempo válido. Por ejemplo, Entrada 11 25 11 25 11 59 23 59 36 59 59 59 Salida 11 25 11 26 12 0 0 0 37 0 0 0 A.3.24 Escriba una función void anterior_fecha (Pint dia, Pint mes, Pint anyo, int ndias); que reciba a través de los parámetros dia, mes y anyo una fecha válida y devuelva a través de los mismos la fecha anterior en ndias dias. Por ejemplo, Práctica 3: Funciones y Procedimientos Entrada 6 12 6 12 3 1 3 3 1998 1998 1999 2000 13 5 10 5 5 Salida 1 12 26 11 29 12 27 2 A.3.25 Escriba una función que imprima la pirámide 1 121 12321 1234321 123454321 La declaración de la función será la siguiente: void piramide (int niveles); siendo niveles el número de filas de la pirámide (5 en el ejemplo). 1998 1998 1998 2000