Unidad VI Funciones en C 1. Funciones estándar del C y provistas por el compilador. 2. Funciones definidas por el usuario. 3. La programación estructurada. 4. Paso de parámetros por valor. 5. Paso de parámetros por referencia. 6. División de programas en librerías. 7. Funciones recursivas. 8. Ejercicios. Página 1 Unidad VI Funciones en C Las aplicaciones escritas en lenguaje C constan de una ó más funciones siendo obligatoria la función denominada main( ). main( ) es la función principal donde empieza la ejecución de las instrucciones que componen el programa. Para utilizar otras funciones, se puede invocar las funciones estándar (propias del estándar del C y provistas por el compilador) o las funciones definidas por el usuario (desarrolladas por el programador). 1. Funciones estándar del C y provistas por el compilador Las funciones estándar incluyen operaciones de entrada y salida de datos, manejo de archivos, funciones matemáticas, de conversión de tipos de datos, etc. Pueden ser utilizadas incluyendo el archivo de cabecera correspondiente que tiene la extensión .h y que se encarga de hacer referencia a una librería donde está implementada dicha función. Ej. Para utilizar printf( ) y scanf( ) es necesario incluir el archivo stdio.h. Ej. #include <stdio.h> void main( void ) { int Entero, Cuadrado; printf("Introduzca un número entero: "); scanf("%i", &Entero); Cuadrado = Entero * Entero; printf("El cuadrado de %i es %i", Entero, Cuadrado); } No todas las funciones provistas por un compilador son estándar del C. Es decir, algunos compiladores disponen de funciones adicionales para facilitar la vida del programador. Si bien esto no parece ser malo también tiene su contraparte, y esto es que el código sea menos portable y dependiente del mismo compilador. Por ejemplo el compilador del Turbo C++ provee la función clrscr( ) y hay que hacer la inclusión del archivo de cabecera conio.h. Sin embargo, esta función no es parte del ANSI C y esto significa que no estará disponible en otros compiladores como Visual C++ (para desarrollar en C bajo Windows) o DJGPP (para desarrollar en C bajo Linux). Así es que, es mejor utilizar funciones estándar para que el código sea portable y pueda ser compilado en distintas plataformas. Generalmente las funciones reciben uno o más datos de entrada. Los datos se transforman y se retorna un valor que es el resultado de dicha transformación. Ej. Si se desea obtener la raíz cuadrada de un número se puede incluir el archivo de cabecera math.h y utilizar la función sqrt( ) de la siguiente manera: Página 2 Computación para Ingenierías I Ing. Franz Mercado Lorberg Ej. #include <stdio.h> #include <math.h> void main( void ) { int Resultado; Resultado = sqrt( 9 ); // La raíz cuadrada de 9 se calculará // y se almacenará en la variable // Resultado printf("La raíz de 9 es %5.2f ", Resultado); } Se puede representar gráficamente las funciones main( ) y sqrt( ) de la siguiente manera: main( ) 9 3 sqrt( ) Analizando solo sqrt( ): 9 sqrt( ) 3 En general: Dato de Entrada O también: Funcion( ) Dato de Salida Datos de Entrada Funcion( ) Dato de Salida Página 3 Unidad VI Funciones en C a) Funciones Matemáticas Para utilizar estas funciones debe incluirse el archivo de cabecera math.h. Función pow( x, y ) Descripción Ejemplo x a la potencia y. Cuadrado = pow( Numero, 2 ); sqrt( x ) Raíz cuadrada de x. Raiz = sqrt( 81 ); abs( x ) Valor absoluto de x (enteros). Absoluto = abs( -798 ); // 798 fabs( x ) Valor absoluto de x (flotantes). exp( x ) Exponencial de ex. log( x ) Logaritmo natural de x. log10( x ) Logaritmo base 10 de x. floor( x ) Redondea x hacia abajo. Res = floor( 2.54 ); // 2 ceil( x ) Redondea x hacia arriba. Res = ceil( 2.54 ); // 3 b) Funciones Trigonométricas Debe incluirse el archivo de cabecera math.h. El dato que se envía a la función debe estar en radianes. Función Descripción Ejemplo sin( x ) Seno de x. Resultado = sin( 0 ); // 0 cos( x ) Coseno de x. Resultado = cos( 0 * 3.14159 / 180 ) // coseno de 0° es 1 tan( x ) Tangente de x. asin( x ) Arco seno de x. acos( x ) Arco coseno x. atan( x ) Arco tangente de x. sinh( x ) Seno hiperbólico de x. cosh( x ) Coseno hiperbólico de x. tanh( x ) tangente hiperbólica de x. c) Funciones para clasificar caracteres Debe incluirse el archivo de cabecera ctype.h. Función Descripción Ejemplo isalnum( x ) x es un alfabético o dígito. Resultado = isalnum( ′A′ ); // 1 isalpha( x ) x es un alfabético. Resultado = isalpha( ′5′ ); // 0 Página 4 Computación para Ingenierías I Ing. Franz Mercado Lorberg Función Descripción isascii( x ) x es un carácter ASCII. iscntrl( x ) x es un carácter de control. isdigit( x ) x es un dígito. isgraph( x ) x es un carácter que se grafica. islower( x ) x es un carácter en minúsculas. isupper( x ) x es un carácter en mayúsculas. isspace( x ) x es un espacio en blanco. isspunct( x ) x es un carácter de puntuación. Ejemplo Resultado = isdigit( ′8′ ); // 1 Resultado = isupper( ′m′ ); // 0 d) Funciones para conversión de tipos Debe incluirse el archivo de cabecera stdlib.h. Función Descripción atoi( x ) Convierte x de carácter a entero. Resultado = atoi( ′9′ ); // 9 atof( x ) Resultado = atof( ′5′ ); // 5.000000 fcvt( … ) Convierte x de carácter a flotante. Convierte x de carácter a entero largo. Convierte x de flotante a texto. itoa( … ) Convierte x de entero a texto. atol( x ) Ejemplo e) Funciones de números aleatorios Debe incluirse el archivo de cabecera stdlib.h. Función randomize( ) random( x ) rand( ) Descripción Cambia la semilla que genera los números al azar. Macro que genera un número entre 0 y x–1 . Genera un número entre pseudoaleatorio entre 0 y RAND_MAX. Ejemplo randomize( ); Numero = random(20); // Genera un numero entre 0 y 19 Numero = rand( ) % 100; // Genera un numero entre 0 y 99 f) Otras funciones Dependiendo de la función que utilice, se deberá incluir el archivo de cabecera respectivo. Entre los archivos de cabecera que se requerirán están: stdio.h, stdlib.h, dos.h. Importante: Algunas de estas funciones no son estándares del C pero son provistas por el compilador del Turbo C++. Página 5 Unidad VI Funciones en C Función Descripción Finaliza la ejecución del programa. No cierra archivos ni flujos abiertos. Finaliza la ejecución del programa. Cierra archivos y flujos abiertos. Retorna al sistema operativo el valor de x. Obtiene el tiempo del sistema. La variable x debe ser de tipo time_t. Detiene la ejecución del código x milisegundos. Emite un sonido de frecuencia x por el parlante del PC. Detiene el sonido del parlante del PC. abort( ) exit( x ) time( &x ) delay( x ) sound( x ) nosound( ) textcolor( x ) cprintf( … ) textbackground( x ) Cambia a x el color del texto. Muestra un texto en pantalla a color. No utilice las constantes de barra invertida. Cambia a x el color de fondo del texto. Ejemplo abort( ); exit( 0 ); // Finalizo sin problema exit( 1 ); // Finalizo con problema time_t Tiempo; // Declara variable time( &Tiempo ); // Obtiene tiempo delay( 1000 ); // Detiene un segundo la ejecución del código sound( 150 ); // Emite un sonido de frecuencia 150 nosound( ); textcolor( LIGHTRED ); textcolor( 12 ); // El 12 es rojo claro cprintf("El resultado es %i", Res); textbackground( BLUE ); // BLUE es una constante de color 2. Funciones definidas por el usuario Además de las funciones estándar y las provistas por el compilador, el lenguaje C permite que el usuario (programador) pueda desarrollar sus propias funciones. La sintaxis que se utiliza para implementar nuevas funciones es: [TipoDeDato] NombreDeFunción ( [ListaDeParametros] ) { [ DeclaraciónDeVariablesLocales ] [ Instrucción_1; Instrucción_2; … Instrucción_N; ] } [ return [constante_variable_estructura_puntero] ; ] Importante: • Los elementos que se encuentran entre corchetes [] son opcionales. • Los puntos suspensivos representan "y así consecutivamente". Página 6 Computación para Ingenierías I Ing. Franz Mercado Lorberg • • • • • DeclaraciónDeVariablesLocales se emplea para declarar variables que son conocidas solo dentro de esta función. Recuerde que las variables locales se descargan de memoria cuando termina de ejecutarse la función donde han sido declaradas. La palabra reservada return se utiliza para retornar: Un valor que puede ser el valor de una constante, el contenido de una variable, una estructura, una unión ó un puntero. return también obliga a terminar la ejecución de la función forzando a la salida y dejando de ejecutar el resto de las instrucciones. TipoDeDato es el tipo de dato que devuelve la función por medio de return. ListaDeParametros son los datos que se recibe la función y que se reciben y almacenan en variables locales. Para cada uno de los elementos de la lista de parámetros, se debe especificar el tipo de dato al que corresponde. Cada elemento se separa del elemento siguiente por medio de una coma. Ej. De acuerdo a la sintaxis anterior para implementar funciones, entonces la siguiente función principal es correcta: main( ) { } Ej. Implemente una función que siempre retorne un cero. int Retorna_Cero( ) { return 0; } Observe que la función Retorna_Cero( ) no recibe ningún parámetro en la ListaDeParametros. La única instrucción que se utiliza es la instrucción return que en este caso devuelve una constante numérica que es el 0. Como el cero es un número entero entonces en la sección TipoDeDato se debe especificar que se va a retornar un entero (int). Gráficamente la función puede representarse como: Retorna_Cero( ) 0 Analizando el código: Salida de un dato por medio de TipoDeDato y como la función devuelve un número entero Ingreso de datos por medio de ListaDeParametros y como la función no recibe ningún valor int Retorna_Cero( ) { return 0; } Página 7 Unidad VI Funciones en C Cuando una función no recibe parámetro alguno, se puede utilizar la palabra reservada void como único elemento en la ListaDeParametros. También se utiliza void en TipoDeDato para especificar que la función no devuelve ningún valor. Entonces la función Retorna_Cero( ) puede volver a escribirse de la siguiente manera: int Retorna_Cero( void ) { return 0; } // Esta función no recibe datos y // retorna un entero Ej. Implemente una función que siempre muestre un saludo en la pantalla. void Saludo( void ) { printf("Hola...\n"); // Esta función no recibe datos y // no retorna valor alguno return; } Cuando no se retorna ningún valor, no es necesario utilizar la palabra reservada return: void Saludo( void ) { printf("Hola...\n"); } // Este código sin return es // idéntico al anterior El programa completo con las funciones Retorna_Cero( ), Saludo( ) y main( ) es: #include <stdio.h> int Retorna_Cero( void ) { return 0; } void Saludo( void ) { printf("Hola...\n"); } void main( void ) { int NumA, NumB, NumC; NumA = Retorna_Cero( ); NumB = Retorna_Cero( ); NumC = Retorna_Cero( ); } Saludo( ); printf("Los valores de las variables son %i, %i y %i", NumA, NumB, NumC ); Página 8 Computación para Ingenierías I Ing. Franz Mercado Lorberg El siguiente gráfico representa las llamadas a las funciones Retorna_Cero( ), Saludo( ) main( ), además del envío y recepción de datos entre ellas: y Retorna_Cero( ) 0 main( ) Retorna_Cero( ) 0 Retorna_Cero( ) 0 Saludo( ) Observe que la función Retorna_Cero( ) se implementó una sola vez, pero puede ser utilizada muchas veces para inicializar diferentes variables. Importante: Cuando no se especifica el tipo de dato que va a retornar una función, el lenguaje C asume que el tipo de dato a devolver por la función es de tipo int. Ej. main( ) { } Es el anterior ejemplo no se especificó el tipo de dato devuelto por la función main( ). Entonces, el compilador asume que main( ) debería retornar un entero (int), así es que no generará un mensaje de error pero sí un mensaje de advertencia (warning) similar a este: “Function should return a value”. Lo correcto sería especificar que main( ) es de tipo void, ó también se podría hacer: Ej. main( ) { return 0; } Página 9 Unidad VI Funciones en C 3. La programación estructurada La programación estructurada se basa en el concepto de dividir un problema de programación en problemas más pequeños y más simples de resolver. Si se consigue resolver todos los problemas pequeños entonces se ha resuelto el problema original. Esta metodología es conocida como “Divide y Vencerás”. El lenguaje de programación C implementa esta metodología por medio de funciones. En otros lenguajes como el ADA y el Visual Basic se utilizan procedimientos y funciones para dividir el programa en subprogramas. Donde los procedimientos son porciones de código que se ejecutan y al terminar no retornan ningún valor. En cambio las funciones se ejecutan y al terminar retornan un valor. Es importante tener en cuenta que C no hace ese tipo de distinción, pues todo se basa en funciones. En C las funciones generalmente retornan un dato de determinado tipo, pero también están aquellas funciones que son de tipo void y que no devuelven ningún valor. 4. Paso de parámetros por valor Analice el siguiente código: #include <stdio.h> // Implementación de la función Suma int Suma( int Numero1, int Numero2 ) { int Resultado; Resultado = Numero1 + Numero2; return Resultado; } void main( void ) { int NumA, NumB, Res; printf("Introduzca un número: "); scanf("%i", &NumA ); printf("Introduzca otro número: "); scanf("%i", &NumB ); Res = Suma( NumA, NumB ); } // Llamada a la función Suma. // El resultado devuelto por la // función se almacenará en Res. printf("La suma de %i y %i es %i", NumA, NumB, Res ); Página 10 Computación para Ingenierías I Ing. Franz Mercado Lorberg Suponiendo que una persona ejecuta el programa y que introduce el valor 20 para el NumA y el valor 30 para el NumB, entonces gráficamente se tiene: 20 Suma( ) main( ) 50 30 Analizando el código: Salida de un dato por medio de TipoDeDato y como la función devuelve un número entero Ingreso de datos por medio de ListaDeParametros. La función recibe dos valores enteros int Suma(int Numero1, int Numero2) { int Resultado; } Resultado = Numero1 + Numero2; return Resultado; Cuando se llama a la función Suma( ), se le pasan dos datos que se denominan argumentos de la función y son NumA y NumB. Cuando estos datos son recibidos en la implementación de la función, se almacenan dentro de las variables locales Numero1 y Numero2. Estos datos recibidos se denominan parámetros y este uno de los mecanismos que se utilizan para que una función reciba datos externos desde otra función. Así es como las funciones se comunican entre sí, enviándose datos y retornándose resultados. Este envío y recepción de datos se denomina “paso de parámetros por valor”. Debido a que no se envían los nombres de las variables, en realidad se envía es el valor que está almacenado dentro de las variables. Importante: Observe que en el paso de parámetros por valor las variables locales en main( ) se denominan NumA y NumB pero en la función Suma( ) las variables locales se denominan Numero1 y Numero2. C no tiene problema alguno con los nombres diferentes de las variables enviadas y recibidas, las únicas consideraciones que debe tener son: • • • Si se envían N datos (argumentos), se deben recibir N datos (parámetros). Los tipos de datos de los argumentos debe coincidir con los tipos de datos de los parámetros. El orden de los argumentos debe ser el mismo en los parámetros. Página 11 Unidad VI Funciones en C Ej. #include <stdio.h> // Implementación de la función (parámetros de entrada) void Muestra( char Letra, int Entero, float Flotante ) { printf("Datos: %c y %i y %f", Letra, Entero, Flotante ); } void main( void ) { char MiLetra = 'F'; int MiEntero = 777; float MiFlotante = 5.55; } // Llamada a la función Muestra (argumentos enviados) Muestra( MiLetra, MiEntero, MiFlotante ); ¿Es posible utilizar las sentencias if, if-else if, switch, for, while, do-while en otras funciones además de la función principal main( )? Si es posible. Para comprobar esto, revise los siguientes programas: Ej. Escriba una función a la que se le envíe un carácter y retorne un valor de verdad si el carácter está en mayúsculas. En caso contrario debe retornar un valor de falsedad. #include <stdio.h> #define FALSO 0 #define VERDAD 1 int Es_Mayuscula( char Letra ) { if ( Letra >= 65 && Letra <= 90 ) // 'A' es 65, 'B' es 66, return VERDAD; // ... 'Z' es 90 else return FALSO; } void main( void ) { char MiLetra; } printf("Introduzca un carácter: "); scanf("%c", &MiLetra); if ( Es_Mayuscula( MiLetra ) ) printf("El carácter %c es una mayúscula", MiLetra); else printf("El carácter %c NO es una mayúscula", MiLetra); Página 12 Computación para Ingenierías I Ing. Franz Mercado Lorberg Ej. Escriba una función a la que se le envíe un valor entero positivo y devuelva la sumatoria del número y sus predecesores. #include <stdio.h> int Sumatoria( int Cantidad ) { int Contador, Suma = 0; for( Contador = 1 ; Contador <= Cantidad ; Contador++ ) Suma += Contador; } return Suma; void main( void ) { int Numero, Resultado; printf("Introduzca un número: "); scanf("%i", &Numero); if ( Numero > 0 ) { Resultado = Sumatoria( Numero ); printf("La sumatoria de los %i primeros enteros es %i", Numero, Resultado); } else printf("Debe introducir solo números positivos"); } Importante: Para finalizar con el paso de parámetros por valor, hay que tener en cuenta que cuando se envían los argumentos y se transforman en parámetros de entrada de la función, cualquier modificación a los parámetros no afecta el valor de los argumentos originales. Ej. #include <stdio.h> void Muestra_Valor( int Numero ) { while( Numero > 0 ) { printf("El valor es %i\n", Numero); // Decrementando el valor de número Numero--; } } Página 13 Unidad VI Funciones en C void main( void ) { int Numero = 4; } printf("El valor es %i\n\n", Numero); Muestra_Valor( Numero ); printf("\nEl valor es %i\n", Numero); En el anterior ejemplo, la variable Numero de main( ) comienza con un valor de 4. Este valor se envía a la función Muestra_Valor( ) donde el valor es decrementado en cada iteración del bucle (ciclo). Al finalizar la función Muestra_Valor( ), el valor que tiene Numero es 0 pero al volver a desplegar su contenido en main( ) se puede apreciar que aún mantiene el valor original de 4. La respuesta a esto es simple. En realidad se ha utilizado el paso de parámetros por valor y se envían copias de los datos. Cualquier modificación que ocurra a los parámetros afecta únicamente a las copias y no a los valores originales (argumentos). 5. Paso de parámetros por referencia Analice el siguiente código: #include <stdio.h> void Intercambia( int Numero1, int Numero2 ) { int Auxiliar; } Auxiliar = Numero1; Numero1 = Numero2; Numero2 = Auxiliar; void main( void ) { int NumA = 55, NumB = 77; printf("Los valores son: %i y %i\n", NumA, NumB); Intercambia( NumA, NumB ); // Esta función intercambia el // contenido de ambas variables. printf("Ahora los valores son: %i y %i", NumA, NumB); } En este ejemplo, inicialmente NumA tiene un valor de 55 y NumB tiene un valor de 77. Después de enviar estos datos a la función Intercambia( ), NumA debería contener 77 y NumB 55. Página 14 Computación para Ingenierías I Ing. Franz Mercado Lorberg Sin embargo, esto no funcionó como se esperaba. NumA y NumB mantienen los valores originales que tenían antes de ser enviados a la función Intercambia( ). Pero, ¿Por qué ocurrió esto si no es lo que se deseaba? La respuesta es que se ha utilizado el mecanismo de paso de parámetros por valor. En la función Intercambia( ) los parámetros recibidos son copias de los datos. Entonces, cuando la función intercambia los valores está trabajando sobre las copias. Los valores originales están seguros en la función main( ) (argumentos originales). Para resolver este problema, sería de gran utilidad el poder enviar los datos originales para que las modificaciones a las variables afecten a los valores originales. Este es otro tipo de mecanismo para el envío y recepción de datos entre funciones y se denomina “paso de parámetros por referencia”. Para implementar el paso de parámetros por referencia se deben utilizar los operadores unitarios de punteros & (ampersand) y * (asterisco), donde: & Obtiene la dirección de memoria del operando. * Accede al contenido de la dirección almacenada en el operando. Los operadores & y * son complementarios. Esto significa que si se utiliza un operador, luego deberá utilizarse el operador complementario. Ej. Volviendo a escribir el código de Intercambia( ): #include <stdio.h> void Intercambia( int *Numero1, int *Numero2 ) { int Auxiliar; Auxiliar = *Numero1; *Numero1 = *Numero2; *Numero2 = Auxiliar; } void main( void ) { int NumA = 55, NumB = 77; printf("Los valores son: %i y %i\n", NumA, NumB); Intercambia( &NumA, &NumB ); // Esta función intercambia el // contenido de ambas variables. printf("Ahora los valores son: %i y %i", NumA, NumB); } Página 15 Unidad VI Funciones en C Observe que en el anterior ejemplo, se utiliza el & (ampersand) al momento de enviar los argumentos en main( ). Cuando los datos son recibidos como parámetros de entrada y son almacenados dentro de variables en la función Intercambia( ), se utiliza de manera complementaria el operador * (asterisco). Hay que recalcar las variables de entrada Numero1 y Numero2, ahora se llaman *Numero1 y *Numero2. Este mecanismo permite que las funciones envíen y reciban los valores originales almacenados en los argumentos de la función. Gráficamente se tiene: 55 77 Intercambia( ) main( ) 77 55 Analizando el código: No hay salida de un dato por medio de TipoDeDato, entonces es de tipo void Ingreso y salida de datos por medio de ListaDeParametros void Intercambia( int *Numero1, int *Numero2 ) { int Auxiliar; } Auxiliar = *Numero1; *Numero1 = *Numero2; *Numero2 = Auxiliar; Importante: El paso de parámetros por referencia permite que una función retorne más de un resultado. Ej. Escriba el código fuente en C para enviar un número a una función y retornar los siguientes tres números que son correlativos al número enviado. #include <stdio.h> void Siguientes_Tres( int Num, int *Num1, int *Num2, int *Num3 ) { *Num1 = Num + 1; *Num2 = Num + 2; *Num3 = Num + 3; } Página 16 Computación para Ingenierías I Ing. Franz Mercado Lorberg void main( void ) { int Num, NumA, NumB, NumC; printf("Introduzca un número entero: "); scanf("%i", &Num); Siguientes_Tres( Num, &NumA, &NumB, &NumC ); } printf("Los siguientes valores a %i ", Num); printf("son: %i, %i y %i", NumA, NumB, NumC); Observe que en los argumentos de la función se tiene: • El caso del argumento Num es un paso de parámetros por valor. Por lo tanto, se está enviando una copia y cualquier modificación a la copia no afecta al valor original. • El caso de los argumentos &NumA, &NumB y &NumC es un paso de parámetros por referencia. Se envían los valores originales y se deben utilizar los operadores & y *. Si la función Siguientes_Tres( ) cambia los valores de los parámetros, entonces se están modificando los valores originales y así serán retornados a la función main( ). • No es necesario que los nombres entre los argumentos y los parámetros sean los mismos. Solo debe verificar la cantidad de argumentos y parámetros, los tipos de datos y el orden. • Es posible que una función envíe argumentos por valor y por referencia al mismo tiempo. Ej. Siguientes_Tres( Num, &NumA, &NumB, &NumC );. Esto significa que los parámetros recibidos por valor no serán modificados por la función, pero los parámetros por referencia sí serán modificados por la función. Ejercicio: ¿Que significan las siguientes llamadas a funciones? a) Calcula_Areas( Base, Altura, &Area_Rectangulo, &Area_Triangulo ); b) scanf ( "%i", &Contador ); c) printf( "%i", Contador ); 6. División de programas en librerías Ahora que sabemos que el programador puede escribir sus propias funciones, que las puede utilizar muchas veces y con argumentos distintos. Sería muy conveniente que por cada proyecto de programación que vaya a desarrollar, no tenga que implementar las mismas funciones una y otra y otra vez. Más bien, que implemente las funciones una sola vez, que las almacene dentro de otro archivo y que dicho archivo pueda ser incluido posteriormente (como si fueran las librerías propias del programador). La manera más simple de realizar esto consiste en separar las funciones y guardarlas en un archivo *.h (* significa cualquier nombre de archivo y .h que la extensión del archivo debe ser de este tipo). Luego el archivo *.c que contiene la función principal main( ) deberá incluir al archivo *.h. Página 17 Unidad VI Funciones en C Antes de trabajar con archivos *.h es necesario considerar un nuevo concepto que es el de prototipo de función. Un prototipo de función permite: • Comunicarle al compilador la existencia de una función que no necesariamente estará implementada antes de la función main( ). • Especificarle al compilador la cantidad de parámetros de la función y sus respectivos tipos de datos. • Especificarle al compilador el tipo de dato retornado por la función. Ahora veamos como se trabaja con el prototipo de función: #include <stdio.h> // Prototipos de funciones int Suma ( int Numero1, int Numero2 ); int Resta( int Numero1, int Numero2 ); void main( void ) { int NumA, NumB, ResSuma, ResResta; printf("Introduzca un número: "); scanf("%i", &NumA ); printf("Introduzca otro número: "); scanf("%i", &NumB ); ResSuma = Suma( NumA, NumB ); ResResta = Resta( NumA, NumB ); } // Llama a Suma // Llama a resta printf("La suma de %i y %i es %i", NumA, NumB, ResSuma ); printf("La resta de %i y %i es %i", NumA, NumB, ResResta ); // Implementación de la función Suma int Suma( int Numero1, int Numero2 ) { return (Numero1 + Numero2); } // Implementación de la función Resta int Resta( int Numero1, int Numero2 ) { return (Numero1 - Numero2); } Observe que el prototipo de función es similar a la primera línea de la implementación de la función. La única diferencia es que el prototipo lleva un punto y coma al final. El prototipo debe estar antes que la implementación de la función y antes que la función principal. Recuerde que con el prototipo de función ya se puede implementar la función antes o después de main( ). Página 18 Computación para Ingenierías I Ing. Franz Mercado Lorberg Importante: Una buena práctica de programación consiste tener un prototipo de función por cada una de las funciones que se implementen. Con el concepto de prototipos de funciones, separemos el código en dos archivos: Archivo MisFunc.h: // Este archivo contiene solo los prototipos // y las implementaciones de las funciones // Prototipos de funciones int Suma ( int Numero1, int Numero2 ); int Resta( int Numero1, int Numero2 ); // Implementación de la función Suma int Suma( int Numero1, int Numero2 ) { return (Numero1 + Numero2); } // Implementación de la función Resta int Resta( int Numero1, int Numero2 ) { return (Numero1 - Numero2); } Archivo Programa.c: // Este archivo contiene la función principal // y la inclusión del archivo *.h #include <stdio.h> #include "MisFunc.h" void main( void ) { int NumA, NumB, ResSuma, ResResta; printf("Introduzca un número: "); scanf("%i", &NumA ); printf("Introduzca otro número: "); scanf("%i", &NumB ); ResSuma = Suma( NumA, NumB ); ResResta = Resta( NumA, NumB ); } // Llama a Suma // Llama a resta printf("La suma de %i y %i es %i", NumA, NumB, ResSuma ); printf("La resta de %i y %i es %i", NumA, NumB, ResResta ); Con este método se implementan las funciones en varios archivos *.h y estos archivos son incluidos cuando se requiere una función que esté implementada ahí. Página 19 Unidad VI Funciones en C Importante: La inclusión del archivo *.h que ha sido desarrollada por el usuario se especifica así: #include "Nombre.h". Observe que al utilizar las comillas dobles se le está especificando al compilador que busque este archivo en la misma carpeta donde se encuentra el archivo *.c (Ej. En la carpeta bin). Cuando se utiliza #include <Nombre.h> se le especifica al compilador que primero busque el archivo *.h en la carpeta include. Importante: Una misma función no debe estar implementada en dos archivos *.h distintos. Esto es así porque, cuando el compilador intenta enlazar los dos archivos *.h no sabrá que función utilizar. 7. Funciones recursivas La recursividad es una técnica que se utiliza para resolver problemas del tipo “Divide y vencerás” y es especialmente útil para la inteligencia artificial. Un problema resuelto con recursividad, también puede ser resuelto con iteraciones (bucles o ciclos). Una función es recursiva cuando cumple los siguientes puntos: • La función tiene una sección para llamarse a sí misma (recursividad). • La función tiene una sección para detener los llamados (condición(es) base). • La función tiene una sección que al volver hacia atrás (backtrack) se muestran datos ó se realizan cálculos. • No hay una gran cantidad llamados recursivos y además son finitos. Ej. Implemente una función recursiva en C para mostrar en pantalla la serie: 1, 2, 3, 4, 5. #include <stdio.h> void Muestra( int Numero ) { if ( Numero == 0 ) // Condición base. Si se cumple, la return; // func. deja de llamarse a sí misma else { // Recursividad. La func. se llama a sí misma Muestra( Numero – 1 ); } // Backtrack. Instrucciones pendientes donde se // muestran datos ó se realizan cálculos printf("%i\n", Numero); } void main( void ) { int Num = 5; printf("La serie es:\n"); } Muestra( Num ); Página 20 Computación para Ingenierías I Ing. Franz Mercado Lorberg 8. Ejercicios Resuelva los siguientes problemas: Funciones estándar 1. Por medio de funciones matemáticas calcule: a. El cuadrado de un número. b. El resultado de un número elevado a una potencia. c. La raíz de un número. d. El valor absoluto de un número entero negativo y flotante negativo. e. Los redondeos hacia arriba y hacia debajo de un número flotante. 2. Por medio de funciones trigonométricas calcule: a. El seno de un ángulo. b. El coseno de un ángulo. c. La tangente de un ángulo. d. El arco seno de un ángulo. e. El coseno hiperbólico de un ángulo. 3. Empleando funciones para clasificar caracteres muestre por pantalla si: a. Un carácter leído desde el teclado es un dígito. b. Un carácter leído desde el teclado es alfabético. c. Un carácter es leído desde el teclado alfabético y si esta en mayúscula o minúscula. d. Un carácter leído desde el teclado es un ASCII. e. Un carácter leído desde el teclado es un espacio en blanco. f. Un carácter leído desde el teclado es un símbolo de puntuación. 4. Empleando funciones de conversión de tipo muestre el resultado de convertir: a. Un texto a un número entero. b. Un texto a un número flotante. c. Un texto a un número entero largo. d. Un entero leído desde el teclado en texto. e. Un flotante leído desde el teclado en texto. 5. Utilice a. b. c. d. e. f. g. otras las funciones para: Generar 20 números al azar y mostrarlos en pantalla. Emular el lanzamiento simultáneo de cinco dados. Emular la salida de una carta entre el 1 al 10, J, Q, K, A y que tenga uno de los palos Corazón, Trébol, Espada, Diamante. Muestre en pantalla la carta obtenida al azar. Mostrar 50 números al azar en distintas coordenadas de la pantalla (80 columnas por 50 filas) y con distintos colores (desde el 1 hasta el 5). Generar 10 sonidos (tono y duración son generados al azar y no son más extensos de 1.5 segundos). Obtenga el tiempo T1. Ejecute un bucle de 3,000,000,000 repeticiones y obtenga un tiempo T2. Calcule y muestre la cantidad de segundos de diferencia que existen entre T1 y T2. Desarrollar un pequeño juego de adivinanza de un número que estará entre 10 y 100. El programa generará un número al azar y el jugador deberá ir probando Página 21 Unidad VI Funciones en C hasta encontrar cuál ese número generado. Para que sea más fácil de adivinar, el programa indicará si el número que introduce el jugador es más bajo ó más alto con respecto al generado. Cuando el jugador encuentre el número se escuchará una pequeña melodía por el parlante de la computadora y se congratulará al jugador. Funciones definidas por el usuario 1. Implemente una función que reciba tres números flotantes y devuelva como resultado el resultado de la suma de los tres números. 2. Escriba una función que reciba tres números flotantes y devuelva el mayor de ellos. 3. Desarrolle una función que reciba una temperatura en grados Centígrados y retorne la temperatura respectiva en grados Fahrenheit. Además de otra función que reciba una temperatura en grados Fahrenheit y retorne la temperatura respectiva en grados Centígrados. 4. Escriba una función que reciba un número N y muestre los siguientes 3 números impares. La función no debe retornar nada. Ej. Recibe 5 y muestra 7, 9, 11. Ej. Recibe 22 y muestra 23, 25, 27. 5. Implemente una función que reciba un número entero y devuelva el carácter ‘N’ si el número es Negativo, ‘P’ si el número es positivo y ‘O’ si es neutro. 6. Desarrolle una función que reciba un carácter y retorne el siguiente carácter en la pantalla. Por ejemplo, si la función recibe una ‘M’ devolverá una ‘N’. 7. Escriba una función que reciba un número entero N y devuelva la suma de los N primeros números enteros. Ej. Si N es 25 entonces la función devuelve la suma de 1 + 2 + 3 + … + 24 + 25. 8. Escriba una función que reciba un número N entero mayor o igual a 0 y que devuelva el factorial de dicho número. Ej. Si N es 5 entonces la función devuelve el producto de 5 * 4 * 3 * 2 * 1. 9. Implemente una función que reciba un número entero largo y que retorne la cantidad de dígitos que conforman dicho número. Ej. Si el número es 567243 entonces este consta de 6 dígitos. 10. Desarrolle una función que reciba un número entero largo y que retorne el entero invertido. Ej. Si el número es 42317, al invertirlo será 71324. 11. Escriba una función para calcular la serie ( N debe ser mayor a 5 ): 12 + 2 2 + 3 2 + ... + N 2 Página 22