Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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;
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 ) { ... }
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.
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.
(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.
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;
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.
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.