Programación Estructurada (en C) Dr. Eric Jeltsch F. Cuando se invoca a una función, y se tenga solamente acceso a los valores de sus argumentos, y NO a los argumentos mismos, se dirá que es una llamada por valor. En C todas las llamadas son por valor, es decir, es imposible que una función llamada tenga acceso directo a un objeto definido en la función. Variables locales Las variables que declaramos justo al principio del cuerpo de una función son variables locales. Este programa, por ejemplo, declara dos variables locales para calcular la sumatoria de los primeros 10, nº natural. #include <stdio.h> int sumatoria(int a, int b) { int i, s; // Variables locales a sumatoria. s = 0; for (i=a; i<=b; i++) s += i; return s; } int main(void) { int i; // Variable local a main. for (i=1; i<=10; i++) printf ("Sumatorio de los %d primeros n´umeros naturales: %d\n", i, sumatoria(1, i)); getch(); return 0; } Las variables locales i y s de sumatoria() sólo “viven” durante las llamadas a sumatoria(). Las variables locales sólo son visibles en el cuerpo de la función en la que se declaran; por tal motivo se dice que ése es su ámbito. Variables globales Las variables globales se declaran fuera del cuerpo de cualquier función y son accesibles desde cualquier punto del programa posterior a su declaración. Este fragmento de programa, por ejemplo, define una variable global i y una variable local a main con el mismo identificador: #include <stdio.h> int i = 1; // Variable global i. int doble(void) { i *= 2; // Referencia a la variable global i. return i; // Referencia a la variable global i. } int main(void) { int i; // Variable local i. ___________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 1 Programación Estructurada (en C) Dr. Eric Jeltsch F. for (i=0; i<5; i++) // Referencias a la variable local i. printf ("%d\n", doble());//Ojo: el valor corresponde a la i global. getch(); return 0; } Fijarse en la pérdida de legibilidad que supone el uso del identificador i en diferentes puntos del programa: hemos de preguntarnos siempre si corresponde a la variable local o global. Por lo mismo, No se aconseja el uso generalizado de variables globales en los programas. #include <stdio.h> int contador ; // Variable global. void fija(int a) { contador = a; } int decrementa(int a) { contador -= a; return contador ; } void muestra(int contador) { printf ("[%d]\n", contador); } void cuenta_atras(int a) { int contador; for (contador=a; contador >=0; contador--) printf ("%d ", contador); printf ("\n"); } int main(void) { int i; contador = 10; i = 1; while (contador >= 0) { muestra(contador); cuenta_atras(contador); muestra(i); decrementa(i); i *= 2; } getch(); return 0; } ___________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 2 Programación Estructurada (en C) Dr. Eric Jeltsch F. Funciones sin parámetros Por otra parte, en el contexto de las funciones, un uso típico de ellas, en donde no es necesario usar parámetros es por ejemplo, la lectura de datos por teclado que deben satisfacer una serie de restricciones. Esta función, por ejemplo, lee un número entero de teclado y se asegura de que sea par: int lee_entero_par(void) { int numero; scanf ("%d", &numero); while (numero % 2 != 0) { printf ("El n´umero debe ser par y %d no lo es.\n", numero); numero = scanf ("%d", &numero); } return numero; } int main(void) { int i; int contador = 10; i = 1; while (contador >= 0) { lee_entero_par(); i *= 2; } getch(); return 0; } Este otro programa muestra por pantalla los 10 primeros números primos. La función siguienteprimo() genera cada vez un número primo distinto. Gracias a la variable global ultimoprimo() la función “recuerda” cuál fue el último número primo generado. #include <stdio.h> int ultimoprimo = 0; int siguienteprimo(void) { int esprimo , i; do { ___________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 3 Programación Estructurada (en C) Dr. Eric Jeltsch F. ultimoprimo++; esprimo = 1; for (i=2; i<ultimoprimo/2; i++) if (ultimoprimo % i == 0) { esprimo = 0; break; } } while (!esprimo); return ultimoprimo; } int main(void) { int i; printf ("Los 10 primeros n´umeros primos\n"); for (i=0; i<10; i++) printf ("%d\n", siguienteprimo()); getch(); return 0; } Otro uso típico es la presentación de menús de usuario con lectura de la opción seleccionada por el usuario: int menu_principal(void) { int opcion; do { printf printf printf printf ("1) ("2) ("3) ("4) Alta usuario\n"); Baja usuario\n"); Consulta usuario\n"); Salir\n"); printf ("Opcion: "); scanf ("%d", &opcion); if (opcion < 1 || opcion > 4) printf ("Opcion no valida.\n"); } while (opcion < 1 || opcion > 4); return opcion; } Procedimientos o función o ambas. Un procedimiento es una función que no devuelve valor alguno. Los procedimientos provocan efectos laterales, como imprimir un mensaje por pantalla, modificar variables globales o modificar el valor de sus parámetros. Los procedimientos en C se declaran como funciones con tipo de retorno void. Por ejemplo: #include <stdio.h> void saludos(void) { printf ("Hola, mundo.\n"); } En un procedimiento puedes utilizar la sentencia return, pero sin devolver valor alguno. Cuando se ejecuta una sentencia return, finaliza inmediatamente la ejecución del procedimiento. ___________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 4 Programación Estructurada (en C) Dr. Eric Jeltsch F. Paso de parámetros (Parámetros escalares: paso por valor) Aunque modifique el valor de un parámetro escalar en una función o procedimiento, el valor de la variable pasada como argumento permanece inalterado. La función bits del siguiente programa, por ejemplo, modifica en su cuerpo el valor de su parámetro num: #include <stdio.h> int bits(unsigned int num ) { int b = 0; do { b++; num /= 2; } while (num > 0); return b; } int main(void) { unsigned int numero ; int bitsnumero; printf ("Introduce un entero positivo: "); scanf ("%u", &numero ); bitsnumero = bits(numero ); printf ("Hay %d bits en %u.\n", bitsnumero, numero ); getch(); return 0; } Al ejecutar el programa y teclear el número 128 se muestra por pantalla lo siguiente: Introduce un entero positivo: 128 Hay 8 bits en 128. Como puede ver, el valor de numero permanece inalterado tras la llamada a bits, aunque en el cuerpo de la función se modifica el valor del parámetro num (que toma el valor de numero en la llamada). Un parámetro es como una variable local, sólo que su valor inicial se obtiene copiando el valor del argumento que suministramos. Así pues, num no es numero, sino otra variable que contiene una copia del valor de numero. Es lo que se denomina paso de parámetro por valor. Paso de funciones como parámetros Hay un tipo de parámetro especial que puedes pasar a una función: ¡otra función!. Esto en todo caso lo veremos más adelante, para cuando veamos punteros y paso por referencia, para distinguirse del paso por valor. Veamos un ejemplo. En este fragmento de programa se definen sendas funciones C que aproximan numéricamente la integral definida en un intervalo para las funciones matemáticas f(x) = x2 y f(x) = x3, respectivamente: #include <stdio.h> float integra(float a, float b, int n, float (*f)(float)) { int i; float s, x; ___________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 5 Programación Estructurada (en C) Dr. Eric Jeltsch F. s = 0.0; x = a; for (i=0; i<n; i++) { s += f(x) * (b-a)/n; x += (b-a)/n; } return s; } float cuadrado(float x) { return x*x; } float cubo(float x) { return x*x*x; } int main(void) { printf ("Integral 1: %f\n", integra(0.0, 1.0, 10, cuadrado )); printf ("Integral 2: %f\n", integra(0.0, 1.0, 10, cubo )); getch(); return 0; } Finalmente digamos que nunca esta demás el que considere que los #include permiten usar funciones ya escritas e incluidas en grandes colecciones denominadas librerías. Las más usuales son: #include #include #include #include <stdio.h> <conio.h> <stdlib.h> <math.h> permite usar printf, scanf, fread, fwrite, fopen, permite usar clrscr y otras funciones.. permite usar rand permite usar funciones trigonométricas ___________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 6