FUNCIONES - INTRODUCCIÓN


Conceptos relacionados con los subprogramas.

Conceptos relacionados con los subprogramas.
Un programa es una colección de instrucciones que puede ejecutar el procesador. Estas instrucciones están almacenadas en la memoria de la computadora, y ocupan distintos números de bytes, dependiendo de los sus operandos. Por tanto, las instrucciones de programa ocupan bloques de memoria de longitudes irregulares. Estos bloques se almacenan de forma contigua, para no desperdiciar inútilmente espacio, pero esto no quiere decir que el procesador siempre ejecute las instrucciones sucesivamente; en realidad, el procesador va "saltando" de una instrucción a otra, y existen, precisamente, instrucciones de "salto", que hacen que el procesador "salte" a una instrucción que no es la siguiente a la última que ha ejecutado, sino una instrucción que puede estar muy lejos (en memoria) de la anterior.

Este concepto de salto puede hacernos pensar en un método interesante para optimizar programas. Si se estudian detalladamente las instrucciones de un programa, es fácil encontrar que una cierta sucesión de instrucciones se repite en numerosas ocasiones a lo largo del programa. Estos bloques de código aparecen una y otra vez en el programa, siempre iguales. Esto se debe a que hay ciertas tareas de programa, como escribir un carácter en la pantalla, que se ejecutan cientos o miles de veces. ¿Sería posible abreviar el programa, escribiendo una sola vez los bloques que se repiten? Ciertamente, si: podemos descomponer el código de programa en dos grandes colecciones de secuencias de instrucciones: las que se repiten y las que no. Para ejecutar un segmento de código que se repite, basta con "saltar" a su comienzo y, finalizado el segmento, "saltar" a la instrucción siguiente. En la imagen, el lado izquierdo muestra (en rojo) el código que se repite en varias ocasiones. A la derecha se muestra el resultado de almacenar en RAM una sola vez el código que antes se repetía. Para obtener un programa funcionalmente equivalente al primero, pero más corto, basta efectuar los saltos que se muestran. El primero de ellos, marcado con (1), representa la ejecución del bloque repetido por primera vez. Cuando se han ejecutado todas las instrucciones de ese bloque, se efectúa un "salto" a la primera instrucción del resto del programa. El último salto, marcado con (4), representa la ejecución del bloque repetido previa al último bloque del programa.
En primera aproximación, el concepto de función o subprograma es, precisamente, el de un segmento de código que se utiliza muchas veces a lo largo de un programa, y que se separa del resto del programa en su propio bloque, para ejecutarlo cuando convenga sin necesidad de repetirlo.Esta primera aproximación no es una descripción completa del concepto de función: el compilador no solo separa físicamente el bloque de código repetido; además, dota a este bloque de código de dos características adicionales que son las que realmente facilitan la tarea del programador:
  1. Variables locales.
  2. Parámetros formales.

Estos conceptos se detallan más adelante. Veamos un programa que hace uso de una función, para apreciar el "feeling" de las funciones, y después mostraremos de inmediato la sintaxis de las funciones en el lenguaje C.

Ejemplo .- Construir el programa Hola, Mundo empleando una función.

Una posible versión de este programa sería la siguiente:

#include<stdio.h>

/* prototipo de la función decir_hola() */
void decir_hola(int numero_de_veces);

int main(int argc, char * argv) {

	decir_hola(7);
	return 0;
}

/* Declaración de la función decir_hola() */
void decir_hola(int numero_de_veces)
{
	int i;
	printf("******************************\n");
	printf("******************************\n");
	printf("******************************\n");
	for(i=0;i<numero_de_veces;i++)
		printf("Hola por vez número %d\n", i);
	printf("******************************\n");
	printf("******************************\n");
	printf("******************************\n");

	return;
}

/*
******************************
******************************
******************************
Hola por vez número 0
Hola por vez número 1
Hola por vez número 2
Hola por vez número 3
Hola por vez número 4
Hola por vez número 5
Hola por vez número 6
******************************
******************************
******************************


Y ahora otra vez

******************************
******************************
******************************
Hola por vez número 0
Hola por vez número 1
Hola por vez número 2
Hola por vez número 3
Hola por vez número 4
Hola por vez número 5
Hola por vez número 6
Hola por vez número 7
Hola por vez número 8
Hola por vez número 9
Hola por vez número 10
Hola por vez número 11
Hola por vez número 12
Hola por vez número 13
******************************
******************************
******************************
*/

Comentarios .- El segmento de código que se repite en este programa es el contenido en el bloque de la función decir_hola() . Este bloque escribe varias líneas de asteriscos, y después va escribiendo la frase " Hola por vez número N ", haciendo variar el valor de N . En la función main() aparecen dos llamadas (dos saltos, decíamos antes) a decir_hola() , una con el parámetro real 7 y otra con el parámetro real 14, lo cual da lugar a dos ejecuciones del bloque de decir_hola() : una con numero_de_veces igual a 7, y otra con numero_de_veces igual a 14. Estas llamadas a decir_hola() son sólo dos, pero podríamos poner cualquier número de llamadas dentro del bloque de main() . El ahorro de espacio es evidente, aunque, como se verá, esa no es la ventaja más importante que aportan las funciones.

Sintaxis de una función en C
En las versiones actuales de C, para poder utilizar una función o subprograma es preciso declararlo previamente. Esta declaración (y definición, de hecho) consta de dos partes:



La declaración del prototipo tiene la forma siguiente:

Tipo_proporcionado nombre_de_función(lista_de_parámetros);

Y un posible ejemplo sería:

int numero_de_vocales(char * cadena);

en donde:



Por otra parte, la declaración del cuerpo o bloque de la función tiene la forma siguiente:

Tipo_proporcionado nombre_subprograma(lista_de_parametros) /* Encabezado */
{
			/* Cuerpo */

			return expresión;
}


En donde



Notas
  1. Los programas comienzan a ejecutarse en la función main() ; todo programa está formado, como mínimo, por la función main() . Un archivo de código en C sin función main() no es ejecutable, pero puede ser una biblioteca de funciones; de hecho, es frecuente construir y compilar bibliotecas de funciones, que después se utilizarán sin tener que volver a compilarlas y, sobre todo, sin necesidad de conocer el cuerpo de esas funciones. El uso de una biblioteca de funciones exige conocer los prototipos de esas funciones: esa va a ser la misión de los archivos de encabezado, como stdio.h . .
  2. main() posee parámetros que le proporciona el sistema operativo. Concretamente, es frecuente encontrar programas en los que la función main() posee el aspecto siguiente:
    int main(int argc, char * argv[]) {...}
    Los parámetros formales argc y argv sirven para que el sistema operativo pueda pasar a main() argumentos recibidos a través de la línea de órdenes. La mecánica de este proceso se describe en la sección correspondiente . Este es el método que se emplea en las órdenes de shell de los sistemas operativos basados en texto; de hecho, la cola de palabras de los sistemas basados en texto será sustituida por una cola de eventos en sistemas dotados de una interfaz gráfica de usuario (GUI) pero el concepto es bastante paralelo.
  3. Todo programa en C es una colección de funciones y llamadas a funciones. La idea de un programa "monolítico", esto es, de un programa formada por una única y monstruosa función main() carece de sentido, y viola todos los principios de programación estructurada.
  4. Hay un orden para la declaración de variables, prototipos y funciones. Concretamente, el compilador de C espera una organización como la siguiente, teniendo en cuenta que en un programa todo es opcional salvo la función main() :
  5. No se admite la declaración anidada de funciones. Esto es, no está permitido declarar un prototipo y una función dentro de otra función.

Ejercicios iniciales - repetir empleando funciones los ejercicios sobre matrices.

  1. Construir un programa que muestre las opciones necesarias para leer una lista de 10 elementos de tipo int , calcular valores de interés, mostrar valores de interés y salir de programa. Los valores de interés de la lista son el máximo, el mínimo y el valor medio.
  2. Construir un programa que muestre las opciones necesarias para leer dos matrices de dimensiones 2x2 (formadas por elementos de tipo float ), calcular la suma de las matrices, calcular la resta de las matrices, mostrar la suma, mostrar la resta y salir de programa.
  3. Construir un programa capaz de leer un "libro" basado en la siguiente estructura de datos:
    #define PAGINAS 3
    #define LINEAS 4
    #define COLUMNAS 10
    
    char libro[PAGINAS][LINEAS][COLUMNAS];
    
    El programa ofrecerá opciones para leer el libro (de teclado), mostrar una cierta página y salir del programa. Mejora.- Hacer que el programa lea de disco el contenido del libro, preguntando al usuario el nombre del fichero que lo contiene.
  4. Construir un programa que muestre en pantalla un tablero de damas. El programa debe ser capaz de admitir "movimientos"; esto es, partirá de un tablero con blancas y negras en sus posiciones, y permitirá al usuario "mover" fichas, mostrando en pantalla el resultado del movimiento.
    Mejoras
    1. Verificar la corrección de todos los movimientos.
    2. Permitir mover a blancas y a negras.
    3. Hacer que el tablero se muestre siempre en pantalla, en el último estado (después del último movimiento).
    4. (Más complicado)Sugerir movimientos al jugador de blancas. Los movimientos deben ser válidos, y a ser posible comportar alguna ventaja.
  5. Construir un programa que muestre en pantalla un tablero de ajedrez, con características similares a las del ejercicio anterior. El tablero debe ser capaz de admitir movimientos para todas las fichas. Comprobar la validez de los movimientos, y advertir al usuario si no son correctos.
  6. Construir un programa que admita dos vectores de ℜ³ y muestre el producto escalar, el productor vectorial, el módulo de los vectores, el coseno del ángulo que forman y el seno del ángulo que forman.
  7. Construir un programa que admita una matriz 3x3 y un vector de ℜ³ y calcula el producto del vector por la matriz y de la matriz por el vector. Sugerencia: si se llama f a la aplicación definida por la matriz, utilizar un vector de Ker(f) para comprobar que en efecto el resultado de aplicar la matriz al vector es el vector 0.

Soluciones

Ejercicios propuestos

  1. Ejercicio 0801r01.- Se dispone de los coeficientes de una ecuación de segundo grado, de la forma
    ax^2 + bx + c = 0
    Se se pide construir tres funciones, adecuadas para calcular el valor de las raíces de esta ecuación, en función del valor del discriminante d = b^2 - 4.0*a*c . Probar las funciones mediante un programa principal.

  2. Ejercicio 0801r02.- Una función recibe como argumento tres cadenas, que corresponden al nombre, apellidos y teléfono de una persona. Se pide construir la función de tal modo que proporcione como resultado la suma de las longitudes de las tres cadenas.

  3. Ejercicio 0801r03.- Una función recibe como argumento un nombre de usuario y una contraseña. Se pide construir la función de tal modo que proporcione el valor 1 si el nombre y la contraseña son correctos, o bien el valor 0 en caso contrario.

  4. Ejercicio 0801r04.- Una función recibe una cadena que podría ser un número entero. Se pide construir la función de tal modo que proporcione como resultado un 1 si la cadena corresponde a un número entero, o un 0 en caso contrario.

  5. Ejercicio 0801r05.- Una función recibe como argumento un nombre de archivo. Se pide comprobar mediante la función si el nombre posee una extensión correcta.

  6. Ejercicio 0801r06.- Una función recibe como argumento el nombre de un archivo. Se pide construir la función de tal modo que proporcione como resultado la longitud del archivo si éste existe, o bien -1 si el archivo no existe.

  7. Ejercicio 0801r07.- Una función recibe un número entero N como argumento. La función debe proporciona como resultado el puntero de una lista adecuada para almacenar N números enteros, o bien NULL si no se dispone de memoria suficiente.

  8. Ejercicio 0801r08.- Una función recibe dos cadenas como argumento; la primera se llama palabra y la segunda se llama diccionario . Se pide construir la función de tal modo que proporcione el valor 1 si palabra aparece dentro de diccionario , o 0 en caso contrario.