Está en la página 1de 7

Introduccin a las funciones

1. Diseo modular de programas: Descomposicin modular


Hasta ahora hemos estado pensando los pasos que deberamos dar para resolver un cierto problema, y hemos creado programas a partir de cada uno de esos pasos. Esto es razonable cuando los problemas son sencillos, pero puede no ser la mejor forma de actuar cuando se trata de algo ms complicado. A partir de ahora vamos a empezar a intentar descomponer los problemas en trozos ms pequeos, que sean ms fciles de resolver. Esto nos puede suponer varias ventajas: Cada trozo de programa independiente ser ms fcil de programar, al realizar una funcin breve y concreta. El programa principal ser ms fcil de leer, porque no necesitar contener todos los detalles de cmo se hace cada cosa. Podremos repartir el trabajo, para que cada persona se encargue de realizar un trozo de programa, y finalmente se integrar el trabajo individual de cada persona. Esos trozos de programa son lo que suele llamar subrutinas, procedimientos o funciones. En el lenguaje C, el nombre que ms se usa es el de funciones.

2. Conceptos bsicos sobre funciones


En C, todos los trozos de programa son funciones, incluyendo el propio cuerpo de programa, main. De hecho, la forma bsica de definir una funcin ser indicando su nombre seguido de unos parntesis vacos, como hacamos con main. Despus, entre llaves indicaremos todos los pasos que queremos que d ese trozo de programa. Por ejemplo, podramos crear una funcin llamada saludar, que escribiera varios mensajes en la pantalla:
saludar() { printf("Bienvenido al programa\n"); printf(" de ejemplo\n"); printf("Bienvenido al programa\n"); }

Ahora desde dentro del cuerpo de nuestro programa, podramos llamar a esa funcin:
main() { saludar(); }

As conseguimos que nuestro programa sea ms fcil de leer. Como ejemplo, la parte principal de nuestra agenda podra ser simplemente:
leerDatosDeFichero(); do { mostrarMenu(); pedirOpcion(); switch( opcion ) { case 1: buscarDatos(); break; case 2: modificarDatos(); break; case 3: anadirDatos(); break;

3. Parmetros de una funcin


Es muy frecuente que nos interese adems indicarle a nuestra funcin ciertos datos especiales con los que queremos que trabaje. Por ejemplo, si escribimos en pantalla nmeros reales con frecuencia, nos puede resultar til que nos los muestre con el formato que nos interese. Lo podramos hacer as:
escribeNumeroReal( float n ) { printf("%4.2f", n); }

Y esta funcin se podra usar desde el cuerpo de nuestro programa as:


float x; main() { x= 5.1; printf("El primer numero real es: "); escribeNumeroReal(x); printf(" y otro distinto es: "); escribeNumeroReal(2.3); }

Estos datos adicionales que indicamos a la funcin es lo que llamaremos sus parmetros. Como se ve en el ejemplo, tenemos que indicar un nombre para cada parmetro (puede haber varios) y el tipo de datos que corresponde a ese parmetro. Si hay ms de un parmetro, deberemos indicar el tipo y el nombre para cada uno de ellos:
sumar ( int x, int y ) { ... }

4. Valor devuelto por una funcin


Tambin es habitual que queramos que nuestra funcin realice una serie de clculos y nos devuelva el resultado de esos clculos, para poderlo usar desde cualquier otra parte de nuestro programa. Por ejemplo, podramos crear una funcin para elevar un nmero entero al cuadrado as:
int cuadrado ( int n ) { return n*n; } main() { int numero; int resultado; numero= 5; resultado = cuadrado(numero); printf("El cuadrado del numero es %d", resultado); printf(" y el de 3 es %d", cuadrado(3)); }

Podemos hacer una funcin que nos diga cual es el mayor de dos nmeros reales as:
float mayor ( float n1, float n2 ) { if (n1>n2) return n1; else return n2; }

Ejercicios propuestos: Crear una funcin que borre la pantalla dibujando 25 lneas en blanco. No debe devolver ningn valor. Crear una funcin que calcule el cubo de un nmero real (float). El resultado deber ser otro nmero real. Probar esta funcin para calcular el cubo de 3.2 y el de 5. Crear una funcin que calcule cual es el menor de dos nmeros enteros. El resultado ser otro nmero entero.

5. El valor de retorno void. El valor de retorno de main


Cuando queremos dejar claro que una funcin no tiene que devolver ningn valor, podemos hacerlo indicando al principio que el tipo de datos va a ser void (nulo). Por ejemplo, nuestra funcin saludar, que se limitaba a escribir varios textos en pantalla, quedara ms correcta si fuera as:
void saludar() { printf("Bienvenido al programa\n"); printf(" de ejemplo\n"); printf("Bienvenido al programa\n"); }

Hay que tener en cuenta que si no indicamos tipo de datos, el lenguaje C no supondr que no vaya a devolver ningn valor, sino que devolver un valor entero (int). De hecho, la forma habitual de declarar el cuerpo de un programa (main) sera sta, que es equivalente a la que hemos estado usando:
int main() { ... }

Eso quiere decir que main tambin puede devolver un valor, que se leer desde fuera de nuestro programa. Lo habitual es devolver 0 si todo ha funcionado correctamente.
int main() { ... return 0; }

Y devolveramos otro valor si hubiera habido algn problema durante el funcionamiento de nuestro programa (por ejemplo, si no hemos podido abrir algn fichero):
int main() {

FILE* fichero; fichero = fopen("nombre.txt", "rt"); if (fichero == NULL) return 1; ... return 0; }

Este valor se podra comprobar desde el sistema operativo. Por ejemplo, en MsDos y Windows se lee con IF ERRORLEVEL, as:
IF ERRORLEVEL 1 ECHO Ha habido un error en el programa.

Nota: En algunos lenguajes de programacin se llama procedimientos (en ingls procedure) o subrutinas a las funciones que no devuelven ningn valor, y se reserva el nombre funcin para las que s dan un resultado.

6. Variables locales y variables globales


Hasta ahora, hemos declarado las variables antes de main. Ahora nuestros programas tienen varios bloques, as que se comportarn de forma distinta segn donde declaremos las variables. Las variables se pueden declarar dentro de un bloque (una funcin), y entonces slo ese bloque las conocer, no se podrn usar desde ningn otro bloque del programa. Es lo que llamaremos variables locales. Por el contrario, si declaramos una variable al comienzo del programa, fuera de todos los bloques de programa, ser una variable global, a la que se podr acceder desde cualquier parte. Vamos a verlo con un ejemplo. Crearemos una funcin que calcule la potencia de un nmero entero (un nmero elevado a otro), y el cuerpo del programa que la use. La forma de conseguir elevar un nmero a otro ser a base de multiplicaciones, es decir:
3 elevado a 5 = 3 3 3 3 3

(multiplicamos 5 veces el 3 por s mismo). En general, como nos pueden pedir cosas como "6 elevado a 100" (o en general nmeros que pueden ser grandes), usaremos la orden "for" para multiplicar tantas veces como haga falta:
/*---------------------------*/ /* */ /* Ejemplo de funcin con */ /* variables locales */ /* */ /*---------------------------*/ #include <stdio.h> int potencia(int base, int exponente) { int temporal = 1; /* Valor que voy hallando */ int i; /* Para bucles */ for(i=1; i<=exponente; i++) /* Multiplico "n" veces */ temporal *= base; /* Y calculo el valor temporal */ return temporal; /* Tras las multiplicaciones, */ } /* obtengo el valor que buscaba */ main() { int num1, num2; printf("Introduzca la base: "); scanf("%d", &num1); printf("Introduzca el exponente: "); scanf("%d", &num2); printf("%d elevado a %d vale %d", num1, num2, potencia(num1,num2)); }

En este caso, las variables temporal e i son locales a la funcin potencia: para main no existen. Si en main intentramos hacer i=5; obtendramos un mensaje de error. De igual modo, num1 y num2 son locales para main: desde la funcin potencia no podemos acceder a su valor (ni para leerlo ni para modificarlo), slo desde main. En general, deberemos intentar que la mayor cantidad de variables posible sean locales (lo ideal sera que todas lo fueran). As hacemos que cada parte del programa trabaje con sus propios datos, y ayudamos a evitar que un error en un trozo de programa pueda afectar al resto. La forma correcta de pasar datos entre distintos trozos de programa es usando los parmetros de cada funcin, como en el anterior ejemplo.

7. Los conflictos de nombres en las variables


Qu ocurre si damos el mismo nombre a dos variables locales? Vamos a comprobarlo con un ejemplo:
/*---------------------------*/ /* */ /* Dos variables locales */ /* con el mismo nombre */ /* */ /*---------------------------*/ #include <stdio.h> void duplica(int n) { n = n * 2; } main() { int n = 5; printf("n vale %d\n", n); duplica(n); printf("Ahora n vale %d\n", n); }

El resultado de este programa es:


n vale 5 Ahora n vale 5

Por qu? Sencillo: tenemos una variable local dentro de duplica y otra dentro de main. El hecho de que las dos tengan el mismo nombre no afecta al funcionamiento del programa, siguen siendo distintas. El programa se comporta como si duplica fuera as:
void duplica(int x) { x = x * 2; }

es decir, como si ambas tuvieran nombres distintos. Y qu ocurre si una de ellas es una variable global? Retoquemos el ejemplo:
/*---------------------------*/ /* */ /* Variables locales y */ /* globales con el mismo */ /* nombre */ /* */ /*---------------------------*/ #include <stdio.h> int n = 5; void duplica(int n) { n = n * 2; } main() { printf("n vale %d\n", n); duplica(n); printf("Ahora n vale %d\n", n); }

El resultado ser exactamente el mismo: la lnea void duplica(int n) hace que dentro de duplica, n se comporte como una variable local, por lo que los cambios que le hagamos no afectan a la variable global. Y si queremos que se pueda modificar un dato indicado como parmetro? Todava no sabemos como hacerlo (lo veremos en el prximo tema). Por ahora slo sabemos hacerlo devolviendo el nuevo valor con return, con lo que nuestro ltimo ejemplo quedara as:
/*---------------------------*/ /* */ /* Modificar la variable */ /* indicada como parmetro: */ /* devolviendo su valor */ /* */ /*---------------------------*/ #include <stdio.h> int duplica(int n) { return n * 2; }

main() { int n = 5; printf("n vale %d\n", n); n = duplica(n); printf("Ahora n vale %d\n", n); }

8. El orden importa
En general, una funcin debe estar declarada antes de usarse. Por ejemplo, este fuente dara un error en muchos compiladores, porque dentro de main intentamos usar algo llamado duplica, que no se ha mencionado antes:
/*---------------------------*/ /* */ /* Funcin desordenada: */ /* puede no compilar */ /* */ /*---------------------------*/ #include <stdio.h> main() { float n = 5; printf("n vale %f\n", n); n = duplica(n); printf("Ahora n vale %f\n", n); } float duplica(float n) { return n * 2; }

La forma de evitarlo es colocar la definicin de las funciones antes de usarlas (si se puede) o bien incluir al menos su prototipo, la cabecera de la funcin sin incluir los detalles de cmo trabaja internamente, as:
/*---------------------------*/ /* */ /* Prototipo de la funcin */ /* antes de main para que */ /* compile sin problemas */ /* */ /*---------------------------*/ #include <stdio.h> float duplica(float n) ; main() { float n = 5; printf("n vale %f\n", n); n = duplica(n); printf("Ahora n vale %f\n", n); } float duplica(float n) { return n * 2; }

Como curiosidad, si no declaramos la funcin ni su prototipo antes de main, ms de un compilador dar por sentado que es int, de modo que este otro fuente s compilara correctamente en la mayora de sistemas:
main() { int n = 5; printf("n vale %d\n", n); n = duplica(n); printf("Ahora n vale %d\n", n); } int duplica(int n) { return n * 2;

9. Algunas funciones tiles


9.1. Nmeros aleatorios
En un programa de gestin o una utilidad que nos ayuda a administrar un sistema, no es habitual que podamos permitir que las cosas ocurran al azar. Pero los juegos se encuentran muchas veces entre los ejercicios de programacin ms completos, y para un juego s suele

ser conveniente que haya algo de azar, para que una partida no sea exactamente igual a la anterior. Generar nmeros al azar (nmeros aleatorios) usando C no es difcil. Si nos ceimos al estndar ANSI C, tenemos una funcin llamada rand(), que nos devuelve un nmero entero entre 0 y el valor ms alto que pueda tener un nmero entero en nuestro sistema. Generalmente, nos interesarn nmeros mucho ms pequeos (por ejemplo, del 1 al 100), por lo que recortaremos usando la operacin mdulo (%, el resto de la divisin). Vamos a verlo con algn ejemplo: Para obtener un nmero del 0 al 9 haramos x = rand() % 10; Para obtener un nmero del 0 al 29 haramos x = rand() % 30; Para obtener un nmero del 10 al 29 haramos x = rand() % 20 + 10; Para obtener un nmero del 1 al 100 haramos x = rand() % 100 + 1; Para obtener un nmero del 50 al 60 haramos x = rand() % 11 + 50; Para obtener un nmero del 101 al 199 haramos x = rand() % 100 + 101; Pero todava nos queda un detalle para que los nmeros aleatorios que obtengamos sean razonables: los nmeros que genera un ordenador no son realmente al azar, sino pseudoaleatorios, cada uno calculado a partir del siguiente. Podemos elegir cual queremos que sea el primer nmero de esa serie (la semilla), pero si usamos uno prefijado, los nmeros que se generarn sern siempre los mismos. Por eso, ser conveniente que el primer nmero se base en el reloj interno del ordenador: como es casi imposible que el programa se ponga en marcha dos das exactamente a la misma hora (incluyendo milsimas de segundo), la serie de nmeros al azar que obtengamos ser distinta cada vez. La semilla la indicamos con srand, y si queremos basarnos en el reloj interno del ordenador, lo que haremos ser srand(time(0)); antes de hacer ninguna llamada a rand(). Para usar rand() y srand(), deberamos aadir otro fichero a nuestra lista de includes, el llamado stdlib:
#include <stdlib.h>

Si adems queremos que la semilla se tome a partir del reloj interno del ordenador (que es lo ms razonable), deberemos incluir tambin time:
#include <time.h>

Vamos a ver un ejemplo, que muestre en pantalla un nmero al azar entre 1 y 10:
/*---------------------------*/ /* Obtener un nmero al */ /* azar */ /* */ /*---------------------------*/ #include <stdio.h> #include <stdlib.h> #include <time.h> main() { int n; srand(time(0)); n = rand() % 10 + 1; printf("Un nmero entre 1 y 10: %d\n", n); }

Ejercicios propuestos: Crear un programa que genere un nmero al azar entre 1 y 100. El usuario tendr 6 oportunidades para acertarlo.

9.2. Funciones matemticas


Dentro del fichero de cabecera math.h tenemos acceso a muchas funciones matemticas predefinidas en C, como: acos(x): Arco coseno asin(x): Arco seno atan(x): Arco tangente atan2(y,x): Arco tangente de y/x (por si x o y son 0) ceil(x): El valor entero superior a x y ms cercano a l cos(x): Coseno cosh(x): Coseno hiperblico exp(x): Exponencial de x (e elevado a x) fabs(x): Valor absoluto floor(x): El mayor valor entero que es menor que x

fmod(x,y): Resto de la divisin x/y log(x): Logaritmo natural (o neperiano, en base e) log10(x): Logaritmo en base 10 pow(x,y): x elevado a y sin(x): Seno sinh(x): Seno hiperblico sqrt(x): Raz cuadrada tan(x): Tangente tanh(x): Tangente hiperblica (todos ellos usan parmetros X e Y de tipo double) y una serie de constantes como M_E, el nmero e, con un valor de 2.71828... M_PI, el nmero Pi, 3.14159... La mayora de ellas son especficas para ciertos problemas matemticos, especialmente si interviene la trigonometra o si hay que usar logaritmos o exponenciales. Pero vamos a destacar las que s pueden resultar tiles en situaciones ms variadas: La raiz cuadrada de 4 se calculara haciendo x = sqrt(4); La potencia: para elevar 2 al cubo haramos y = pow(2, 3); El valor absoluto: si queremos trabajar slo con nmeros positivos usaramos n = fabs(x); Ejercicios propuestos: Crear un programa que halle cualquier raz de un nmero. El usuario deber indicar el nmero (por ejemplo, 2) y el ndice de la raiz (por ejemplo, 3 para la raz cbica). Pista: hallar la raz cbica de 2 es lo mismo que elevar 2 a 1/3. Crear un programa que resuelva ecuaciones de segundo grado, del tipo ax2 + bx + c = 0 El usuario deber introducir los valores de a, b y c. Pista: la solucin se calcula con x = raz (b2 4ac) / 2a.

También podría gustarte