Funciones Clase 9 Introducción a la Computación Patricia Borensztejn Definición de una función • Esta función calcula el logaritmo en base b de un número x Tipo de retorno Sentencias Parámetros Variables Locales Sentencia return Invocación de una función • La definición de una función no hace que ésta se ejecute. Salvo la función principal, main() (que es invocada por el SO), todas las funciones han de ser invocadas desde alguna función, que incluso puede ser ella misma (recursivas). parámetros reales variable donde se guarda el valor de retorno identificador Tipo de Retorno • Toda función retorna un valor. Si no nos interesa el valor de devolución de la función podemos hacer dos cosas: – Ignorarlo – Declarar el tipo de retorno como tipo void. int main() { saludos(); return 0; } Sentencia return • La sentencia return termina la ejecución de la función, aunque puede haber mas de una dentro del cuerpo de la función, la primera que se ejecuta da fin a la función. • Si no hay sentencia return en una función, este termina con la última sentencia del cuerpo de la función. Variables Locales • Toda variable declarada dentro de la función se llama local porque solo «vive» mientras la función está activada (ejecutándose). • Cuando la función finaliza, las variables locales desaparecen de la memoria… • ¿Como es eso? Porque cada activación de la función utiliza una zona de memoria propia: allí se reserva espacio para sus variables locales, para sus parámetros, para su valor de retorno y también para la dirección de retorno al programa que la invocó. • Cuando termina la función, y antes de volver al programa invocador, se libera toda la zona de memoria usada por ella. Variables Locales Variables Globales • El uso de las variables globales… genera confusión y resta legibilidad a los programas. Como ventaja, se ahorra el paso de parámetros. Parámetros • Una función puede NO tener parámetros: por ejemplo, main(). • Otro ejemplo, rand() definida en stdlib.h int main() { int i; for (i=0; i<10; i++) printf ("%d\n", rand() % 6 + 1 ); } Parámetros • Parámetros escalares: paso por valor • En algunos lenguajes se llama paso por copia, pues efectivamente lo que sucede en el momento de la invocación es que se copia el valor del parámetro real (numero) a la zona de memoria donde está declarado el parámetro de la función: num. • Aunque la función modifique el valor recibido, lo hace sobre otra zona de memoria, por lo tanto no se modifica el valor de la variable escalar perteneciente al programa que invovó a la función. Interface Software-Hardware: La Pila • Las variables locales a la función se implementan en una zona de memoria llamada pila. • La pila de memoria funciona como una pila de platos: solo vemos el que está arriba, y empezamos a lavar desde el último que apilamos hasta el primero. Puede ser que mientras estés lavando los platos en la cocina llegue tu mamá del comedor con MUCHOS mas platos para lavar que apilará sobre el último apilado. Gestión de Memoria : la Pila (1) • Cada función activada tiene en la pila una zona que le es propia: bloque de activación. • Por ejemplo: este es el aspecto de la pila con la zona reservada a la función main() cuando ésta es activada, que contiene dos variables locales: bitsnumero y numero. (ver transparencia 10) Bloques de Activación (2) • Despues que el usuario introdujo el número 128 • Cuando se produce la llamada a la función bits Bloques de Activación (3) • El parámetro num recibe una copia del contenido de numero y la variable b se inicializa en 0 • Después de ejecutar el bucle de bits Bloque de activación (4) • Inmediatamente ANTES de ejecutar la sentencia return, se eliminan las variables locales de la función y queda solamente el valor de retorno • La sentencia return vuelve a la siguiente sentencia despues de la invocación. EL valor de retorno se copia en la variable bitsnumero Interface Software-Hardware • Veamos, para entender el tema de variables locales, parámetros y valores de retorno, como se implementa el código de una función y cómo se activa. • Es decir, veamos el código ensamblador de una función y su llamada, puesto que es el compilador el que genera el código que reserva memoria en la pila. gcc –S bits.c .file .text "bits.c" .def _bits; pushl movl subl movl %ebp %esp, %ebp $16, %esp $0, -4(%ebp) incl movl shrl movl cmpl jne movl leave ret -4(%ebp) 8(%ebp), %eax %eax %eax, 8(%ebp) $0, 8(%ebp) L2 -4(%ebp), %eax .globl _bits .scl 2; .type 32; .endef _bits: ESP L2: BA de bits -4 b EBP EBP +4 @ret a main +8 Copia de numero BA de main (parte) Interface Hardware-Software • Toda función debe construir y destruir su BA: – Salvar registros que utilize (push) – Reservar espacio en la pila (restar a ESP, de 4 en 4) – Obtener los parámetros – Operar, ejecutar… – Copiar a la pila o a registro (aquí es a registro) el valor de devolución de la función – Liberar memoria (leave) – Retornar (return) Variables Globales • Las variables globales residen en una zona especial de memoria, no en la pila. Viven durante toda la ejecución del programa. Es decir, son permanentes. Parámetros vectoriales:paso por referencia • Un parámetro de tipo vector se pasa copiando la dirección del vector. • A esta manera de pasar parámetros se le llama por referencia. • Cuando se pasa la dirección de la estructura, los cambios producidos por la función ocurren sobre la estructura, y por lo tanto, a la vuelta de la función, el vector queda modificado Parámetros Vectoriales • El lenguaje C define que un vector NO se copia elemento a elemento, sino que se copia su dirección. • De esta manera se ven los BA de main y la función. Paso por referencia utilizando punteros • Un puntero es un tipo de dato. Su valor es una referencia o dirección de memoria. • Al declarar una variable de tipo puntero, se debe especificar el tipo del valor apuntado (int, char, long, float, etc). Esto se hace de la siguiente manera: – int * a; // a es un puntero a entero – char* c; // c es un puntero a carácter • Si una función espera un puntero como parámetro, la función que la invoca debe pasarle la dirección de la variable, utilizando para ello el operador & C solo usa paso de parámetros por valor • Asi es. Por eso es que nos complica la existencia teniendo que usar el operador * cada vez que pasamos una variable por referencia. • Paso por referencia en C significa paso por valor de su dirección. Paso de matrices o vectores multidimensionales • Este código no funciona…. • Porque para calcular a[i][j] el compilador debe saber cuantas columnas hay en cada fila…. Y no hay manera de saberlo en la función maximo Matrices o vectores multidimensionales • En general, si el vector tiene N dimensiones, necesitamos pasar como parámetro las N-1 dimensiones. (la primera dimensión no es necesario). • El tipo de retorno de una función NO puede ser un vector (recuerden que no hay asignación para vectores) Sobre archivos, funciones y variables • Todos los objetos en C deberán declararse antes de usarse. • Las variables y funciones no declaradas en el archivo donde son referenciadas o invocadas, deben declararse como externas: – extern int a; – extern int potencia(float x, int y); • Las variables locales no tienen visibilidad fuera de la función que las contiene. Euler 12: ¡Hacerlo con Funciones! The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be: 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ... Let us list the factors of the first seven triangle numbers: 1: 1 3: 1,3 6: 1,2,3,6 10: 1,2,5,10 15: 1,3,5,15 21: 1,3,7,21 28: 1,2,4,7,14,28 We can see that 28 is the first triangle number to have over five divisors. What is the value of the first triangle number to have over five hundred divisors? (500 divisores). Aclaración: se refiere a los divisores, ¡no a los factores primos!