Uncategorized

Programando en C: Funciones

Capítulo 11: Funciones

¿Qué son las funciones?

Las funciones existen para cubrir varias necesidades que el programador acaba teniendo en su día a día.

De hecho, en nuestro caso, tal y como llevamos ahora mismo el ejercicio del curso, veremos que el uso de funciones no supone claras ventajas para mantener nuestro código entendible y mantenerle.

podemos incluso concebir las funciones como pequeños programas dentro de un programa más grande, ya que son bloques de código encapsulados que aceptan una entrada, realizar un cálculo, y devuelven un resultado.

La diferencia es que, si bien el programa original acepta una entrada usualmente proporcionada por el ser humano, y ofrece una salida también usualmente al ser humano, las funciones son pequeños programas que existen dentro del programa principal, y es por esto que, muy generalmente, aceptan datos no del ser humano sino del programa principal, y devuelven datos no al ser humano, sino al programa principal.

por tanto, el concepto principal cuando hablamos de funciones es el de la encapsulación. Es decir, introducir una pieza del código que tenga sentido como bloque, y aislarla del resto del código.

Esto, en un momento dado, puede tener claras y evidentes ventajas.

en primer lugar, es muy común, durante el desarrollo de un programa informático, que haya porciones de código que se repitan.

Las funciones nos proporcionan una posibilidad fantástica de aislar estas porciones en una función, y simplemente llamar a la función tantas veces como sea necesario, centralizando el código, y evitando repeticiones innecesarias que dificultan enormemente la mantenía peligrar del código.

de esta forma, y como podemos imaginar, lo principal que debemos aprender con respecto a las funciones es, en primer lugar, como declararlas, y en segundo lugar, cómo usarlas.

Así que, vamos a poner un primer ejemplo, en el que tendríamos, por ejemplo, un programa que realiza la siguiente función:

main.c
#include <stdio.h>
int main(int argc, char *argv[]) {    printf(«Hola, Jose Vicente \n»); return 0;}

de hecho todo el tiempo hemos estado trabajando con una función principal, una función que se encarga de establecer el punto de entrada del programa, así que, en el fondo, el uso de funciones no no se es extraño en absoluto, ya que, sin quererlo, las llevamos usando desde el principio de nuestra andadura con este lenguaje de programación.

Declaración de funciones

Ahora, lo importante, es que en un momento dado, podemos declarar nuevas funciones.

Dependiendo del lenguaje de programación, podemos declarar nuevas funciones antes o después de la función principal.

Eres de caso, lo que haremos, por compatibilidad, es declarar nuestras funciones antes de la función principal, para que cuando se profesen, el sistema sea consciente primero de nuestras funciones, y luego desuso

Así que el código anterior se transforma en el siguiente código.

main2.c
#include <stdio.h>void saluda(){    printf(«Hola, Jose Vicente \n»);}int main(int argc, char *argv[]) {     return 0;}

Como podemos observar, al declarar una función, en primer lugar, declaramos el tipo de dato te va a devolver la función.

A continuación, establecemos el nombre del programa, siguiendo las reglas de estilo que hemos visto anteriormente.

Lo siguiente que podemos observar, es que las funciones, Mediante la aparición de unos paréntesis, pueden aceptar parámetros. Sin embargo, para este primer ejemplo, vamos a crear una función sin necesidad de poner parámetros.

Deberemos tener en cuenta, eso sí, que aunque la función no acepte parámetros, es obligatorio poner los paréntesis, aunque finalmente se queden vacíos.

Por último, encerrado entre llaves, encontramos el código que se va a ejecutar en el momento en el que se llame a la función.

Así que, finalmente, nuestro código quedaría de la siguiente forma:

main2.c
#include <stdio.h>void saluda(){    printf(«Hola, Jose Vicente \n»);}int main(int argc, char *argv[]) {     return 0;}

Sin embargo, si ejecutamos el código que he descrito anteriormente, podremos comprobar que el programa no hace nada.

Este es un concepto muy importante que debemos entender del uso de el cabo Encapsulación es, y en este caso, del uso de funciones.

En el momento en el que yo creo una cápsula, es decir, en el momento en el que creamos una función, eso no implica que la función se esté ejecutando.

Si nos fijamos, ocurre exactamente lo mismo que cuando declaramos variables.

El hecho de que yo declare una variable, no implica, en absoluto, que yo estoy usando esa variable. Con las funciones ocurre exactamente lo mismo, que yo esté declarando una función no implica, en absoluto, que esté ejecutando esa función.

Lo único que estoy haciendo es introduciendo esa función en la memoria, y preparando el programa para cuando yo la llame, pero el caso es que, de momento, no he llamado a la función

Uso de funciones

Una vez que hemos definido una función, a continuación, probablemente querremos llamarla.

Mediante la invocación de una función, ejecutamos el código que está contiene.

Así que, por ejemplo, en la función principal, vamos a escribir el siguiente código:

main.c
#include <stdio.h>void saluda(){    printf(«Hola, Jose Vicente \n»);}int main(int argc, char *argv[]) {    saluda(); return 0;}

De esta forma, si ahora ejecutamos el programa, cómo podremos comprobar, obtenemos de vuelta la ejecución del código que contiene la función.

De esta forma, hemos visto como declarar en primer lugar una función, y a continuación, utilizarlo

Funciones con parámetros

Lo que hemos visto hasta el momento en cuanto al trabajo con funciones, ya es útil desde el punto de vista en que nos permite aislar porciones de código, y eventualmente, en el caso de que esa porción se repita, nos permite simplificar y reducir la repetición, mediante la definición invocación de funciones.

Sin embargo, los parámetros nos permiten aumentar todavía más la potencia de las funciones mediante la personalización de su función.

La función que hemos declarado anteriormente tiene un único comportamiento es decir, tiene una única posibilidad de ejecución.

Así que, si en lugar de querer una función que me salude a mí, quiero una función y que salude a cualquier otro nombre de persona, tendría que escribir tantas funciones como nombres de personas pudiera imaginar.

Como podemos imaginarnos, esto sería terriblemente ineficiente para lo que son los mecanismos y las estructuras de un lenguaje de programación.

Para esto precisamente existen los parámetros de las funciones. Los parámetros nos permiten personalizar y adaptar el comportamiento de una función, para que se comporte de tantas maneras como necesitemos.

así que, en el ejemplo anterior, voy a introducir un parámetro dentro del paréntesis, al que llamaré nombre.

El código anterior, queda de la siguiente manera:

main4.c
#include <stdio.h>void saluda(char* nombre){    printf(«Hola, %s \n»,nombre);}int main(int argc, char *argv[]) {    saluda(); return 0;}

Si ahora intento ejecutar el código en la función principal de la misma forma que lo había hecho hasta el momento, ahora obtendré un error de vuelta, producido evidentemente por el hecho de que yo ahora estoy prometiendo que la función afecta un parámetro, Pero sin embargo, cuando la invoco, no le estoy pasando este parámetro.

Lo que ocurre en ese momento depende en gran medida de lo permisivo que sea el lenguaje de programación, pero en la mayoría de los casos, el resultado es un error debido a que no estoy invocando la función de la forma correcta, es decir, no la estoy invocando con las mismas reglas que la he definido.

Si ahora introduzco un parámetro de los paréntesis, la función vuelve a funcionar correctamente.

main5.c
#include <stdio.h>void saluda(char* nombre){    printf(«Hola, %s \n»,nombre);}int main(int argc, char *argv[]) {    saluda(«Jose Vicente»); return 0;}

Pero sobre todo, lo más importante, es que puedo llamar varias veces a la función, y en cada una de las ejecuciones, puedo llamar a un parámetro diferente.

Si ahora, dentro de los paréntesis, introducimos diferentes nombres de persona, comprobaremos que tenemos una misma función que puede tener múltiples comportamientos, todo ello basado en los parámetros que tiene la función

Funciones con múltiples parámetros

por supuesto, una función no solo puede aceptar un parámetro, sino que generalmente acepta varios de ellos.

Eres de ejemplo, voy a extender la función para introducir un segundo parámetro, no Merico, que sea la edad.

main6.c
#include <stdio.h>void saluda(char* nombre, int edad){    printf(«Hola, %s, tienes %i años \n»,nombre,edad);}int main(int argc, char *argv[]) {    saluda(«Jose Vicente»,40); return 0;}

Como habremos observado anteriormente, lo único importante al declarar parámetros de funciones, es que exista una coincidencia absoluta entre el nombre del parámetro cuando lo declaramos en los paréntesis, y el nombre del parámetro cuando lo utilizamos dentro del código.

Así que, de esta manera, si introduzco dos parámetros dentro de los paréntesis, y los uso correctamente dentro del código, podremos observar, evidentemente llamando correctamente a la función dentro del método print principal, como, sin ningún problema, la función ahora puede trabajar con dos parámetros

Sobrecarga de parametros

El concepto de sobrecarga tiene, usualmente, connotaciones negativas cuando lo aplicamos a cualquier otro aspecto de la vida.

Cuando sobrecargamos algo, quiere decir que lo estamos cargando con más carga de la que nominalmente puede soportar.

Y como podemos imaginar, cuando cargamos algo con más carga de la que nominalmente puede soportar, lo más probable es que ese objeto se rompa o se parta.

Sin embargo, en el mundo de la programación funcional el concepto de sobrecarga tiene una connotación absolutamente positiva, y es que nos permite hacer que una función se comporte de varias formas diferentes.

Mediante la sobrecarga, podemos definir una misma función para que tenga diferentes comportamientos en base al número de parámetros que va a contener.

main7.c
#include <stdio.h>void saluda(char* nombre, int edad){    printf(«Hola, %s, tienes %i años \n»,nombre,edad);}
void saluda(char* nombre, int edad){    printf(«Hola, %s \n»,nombre);}int main(int argc, char *argv[]) {    saluda(«Jose Vicente»,40); return 0;}

Al igual que ocurre con las variables yo no podría declarar dos veces una función, ya que si intento ejecutar el código, naturalmente dará error.

Sin embargo, lo que sí que puedo hacer, y en ocasiones resulta extremadamente conveniente, es definir varias veces una función, cambiando elementos tales como por ejemplo el tipo de dato devuelto, o el número de parámetros.

así pues, de esta manera, retomando el ejemplo anterior, yo podría desear llamar a la función a veces solo con el nombre, y otras veces con el nombre y la edad.

si no hiciera esto, lo que ocurriría, es que tendría que usar dos funciones diferentes, es decir, dos nombres de función completamente diferenciados.

Sin embargo, si escribo el código que ves a continuación:

main.c

Verás que no hay ningún problema en llamar, en el código principal, unas veces a la función solo introduciendo el nombre como parámetro, y otras veces a la función introduciendo tanto el nombre como la edad.

Por supuesto las funciones no solo admiten una sobrecarga, sino que pueden admitir tantas sobrecargas como necesitemos, con respecto a nuestras necesidades dentro del programa

El ámbito de las variables:

Hay un concepto de vital importancia durante el desarrollo de aplicaciones, qué consiste en controlar el ámbito de las variables. el ámbito hace referencia al contexto en el que esas variables existen

 la regla en cuanto al ámbito de las variables es realmente sencilla. si una variable se declara dentro de una función, s.a. variable sólo existe, y por tanto solo puede usarse, dentro de esa función.

 sí intentamos llamar a esa variable fuera de la función, encontraremos que el programa no reconoce el valor de esa variable, como en el ejemplo siguiente

 sin embargo, si esa variable ha sido declarada fuera de la función, y por tanto, con respecto al lenguaje de programación que estamos cubriendo en esta publicación, ha sido declarada fuera de todas las funciones, pasa a conocerse con el nombre de variable global, y por tanto es una variable que existe, y por tanto se comparte entre las diferentes funciones que comparten un programa

 en definitiva, el conocimiento de la existencia del ámbito de las variables nos permite jugar, ya que hay casos en los que nos interesa que una variable solo exista, solo tenga sentido, solo se pueda modificar desde dentro de una función, y hay otras muchas veces en las que nos interesa que una variable pueda ser modificada a lo largo de las diferentes funciones de un programa 

Ejercicio del curso

Código fuente de nuestro programa se va alargando, y en cierta forma, se va complicando

Es por esto que empezamos a aislar el contenido y la funcionalidad de cada una de las opciones que se le ofrece al usuario, en una función diferente punto en esta fase del ejercicio, antes de la función principal, declaramos una serie de funciones, donde cada una de ellas corresponde a cada una de las opciones que se le proporcionan al usuario. incluso definimos una función que corresponde al propio menú del programa

 una vez que estas funciones han sido declaradas en la parte inicial del programa, esto es, recomendablemente siempre antes de la función principal, a continuación, extraemos el código de cada una de las opciones del usuario, y lo introducimos dentro de cada una de las funciones que hemos creado anteriormente

 por último, en la función de menú principal, en cada una de las opciones, reemplazamos el código original con la llamada a la función correspondiente. al realizar esta acción, podremos comprobar como el código resultante es, de momento, igualmente grande, pero desde luego mucho más legible de lo que era anteriormente

 el último paso, en este caso, consiste simplemente en realizar una llamada a la función de menú principal, desde la propia función principal del programa 

agenda.c
/* Programa agendapor Jose Vicente Carratala */#include <stdio.h>#include <string.h>#define NOMBREPROGRAMA «Programa agenda»#define VERSION «1.0»#define AUTOR «Jose Vicente Carratala»
struct RegistroAgenda{        char nombre[50];        char telefono[50];        char email[50];    };        struct RegistroAgenda agenda[100];void listadoRegistros(){    for(int i = 1; i<=3;i++){        printf(«Registro: %s %s %s \n»,agenda[i].nombre,agenda[i].telefono,agenda[i].email);    }}void introducirRegistro(){    printf(«Vamos a introducir un registro»);}void eliminarRegistro(){    printf(«Vamos a eliminar un registro»);}void buscarRegistro(){    printf(«Vamos a buscar un registro»);}void actualizarRegistro(){    printf(«Vamos a actualizar un registro»);}void menuPrincipal(){     // Mensaje de bienvenida printf(«%s v%s \n»,NOMBREPROGRAMA,VERSION); printf(«%s \n»,AUTOR); printf(«\t 1 – Listado de registros \n»); printf(«\t 2 – Introducir un registro \n»); printf(«\t 3 – Eliminar un registro \n»); printf(«\t 4 – Buscar un registro \n»); printf(«\t 5 – Actualizar un registro \n»); printf(«Tu opcion: «); char opcion = getchar(); printf(«La opción que has seleccionado es: %c \n»,opcion); switch(opcion){     case ‘1’:         listadoRegistros();         break;        case ‘2’:         introducirRegistro();         break;        case ‘3’:         eliminarRegistro();         break;        case ‘4’:         buscarRegistro();         break;     case ‘5’:         actualizarRegistro();         break;        default:            printf(«La opción que has introducido no es válida»);            break; }}int main(int argc, char *argv[]) {
    // Primer registro        strcpy(agenda[1].nombre,»Jose Vicente»);    strcpy(agenda[1].telefono,»12345678″);    strcpy(agenda[1].email,»info@josevicentecarratala.com»);        // Segundo registro    strcpy(agenda[2].nombre,»Juan»);    strcpy(agenda[2].telefono,»53254″);    strcpy(agenda[2].email,»juan@josevicentecarratala.com»);    // Tercer registro    strcpy(agenda[3].nombre,»Jaime»);    strcpy(agenda[3].telefono,»64566″);    strcpy(agenda[3].email,»jaime@josevicentecarratala.com»);    menuPrincipal(); return 0;}

Externalización del contenido del programa en diferentes archivos.

en el desarrollo de aplicaciones informáticas suele aplicarse la regla de » divide y vencerás » con bastante éxito.

aunque existen ocasiones en las que está justificado mantener un código monolítico, una gran pieza de código contenida en un único archivo o en un pequeño conjunto de archivos, por regla general, la recomendación es justo la contraria:

Mantener el código separado en un número razonable y justificado de archivos independientes, para ver esta manera, Mantener el código modular izado, independiente, y más fácilmente mantenerle.

todo el código que hemos desarrollado hasta ahora, está dentro de un único archivo, en el caso tanto de los ejercicios individuales, como en el caso del proyecto continuo que vamos realizando unidad a unidad.

A continuación, se muestra un ejemplo en el que partimos del siguiente código:

main.c
#include <stdio.h>int calculadoraSuma(int operando1, int operando2){    return(operando1 + operando2);}int main(int argc, char *argv[]) {    printf(«El resultado es: %i \n»,calculadoraSuma(5,4)); return 0;}

Como podemos ver, tenemos una función principal, y también tenemos una función que antecede a la función principal, que cumple un cometido concreto, sea cual sea.

una estrategia que podemos cumplir es la de externalizar esa función no principal en un archivo que puede estar adherido al archivo inicial, o puede estar incluido dentro de una carpeta.

para este ejemplo, dentro de la estructura de directorios, dentro de la misma carpeta donde se encuentra el proyecto actual, donde encontramos el código del archivo principal.

Dentro de esta carpeta, creamos una nueva su carpeta, y le ponemos el nombre que queramos, siguiendo las mismas reglas de estilo que estamos utilizando a lo largo de todo el desarrollo.

Por ejemplo, la carpeta puede recibir el nombre de » librerías «

dentro de esta carpeta, creamos un nuevo archivo con la extensión C, donde introduciremos nuestro código. Es decir, cortamos el código de la función no principal desde el archivo mail. C, hasta el archivo que acabamos de crear dentro de la carpeta librerías. Por ejemplo, le ponemos el nombre saludar. C, y pegamos dentro de él el contenido cortado, es decir, la función.

lib/calculadoraSuma.c
int calculadoraSuma(int operando1, int operando2){    return(operando1 + operando2);}

debemos tener cuidado porque, si no estamos trabajando en un entorno de desarrollo integrado, muy probablemente, al haber movido la función a otro archivo con respecto al archivo original, la función principal ya no es capaz de ver dónde está la declaración de la función de saludo.

Esto quiere decir que, si ahora ejecutamos el programa principal, dará error, porque se está invocando a la función, pero no se sabe dónde donde se está declarando la función.

Para conseguir este objetivo, lo que tenemos que hacer es usar las cabeceras para realizar una importación.

Mediante la siguiente importación:

main.c
#include <stdio.h>#include «lib/calculadoraSuma.c»int main(int argc, char *argv[]) {    printf(«El resultado es: %i \n»,calculadoraSuma(5,4)); return 0;}

Estamos indicándole al programa principal que tiene que tomar el código de la carpeta de librerías, y por tanto, tiene que ser consciente de la declaración de la función.

De esta full de esta forma, ahora, si ejecutamos el código, podremos comprobar cómo vuelve a funcionar correctamente, ya que, aunque la función no está físicamente declarada dentro del archivo principal, sí que estás siendo incluida a través de la cabecera, con lo cual, a efectos prácticos de la compilación, es como si estuviera dentro del archivo original del proyecto.

Como nos podemos imaginar, este proceso es repetible tantas veces como sea necesario, para mantener el código lo más modular posible, y por tanto facilitar su comprensión y su mantenimiento y dad

Ejercicio del curso:

Aplicamos este valioso conocimiento para mejorar la mantenibilidad del código que hasta el momento hemos desarrollado

 nuestro proyecto de agenda, en la iteracion anterior, se ha convertido en un programa ciertamente largo. esto suele suceder con mucha frecuencia a medida que vamos desarrollando, diría yo, cualquier tipo de programa punto llega un momento, especialmente cuando estamos usando programación estructurada, en el que el código fuente de nuestra aplicación tiene un número par de líneas que le empieza a resultar incómodo ir de una sección a otra del código, para implementar cambios, mejoras, o nuevas funcionalidades

 cuando este momento llega, es cuando debemos plantearnos implementar alguna metodología para conseguir que el proyecto sea más fácilmente manipulable

 así que en este caso, lo que hacemos es externalizar cada una de las funciones en un archivo diferente, que más adelante será incluido dentro del programa principal

 de esta forma, en primer lugar, creamos una carpeta en la que introducimos nuevos archivos con la extensión c. aunque no es obligatorio, evidentemente, por claridad, cada archivo recibe el nombre de la función que contiene. y cada archivo contiene únicamente, para este ejemplo, una función, aunque debemos tener en cuenta que esto, para este ejercicio, es más una recomendación que una obligación

Por supuesto, a medida que vamos poniendo el código fuente de las funciones en cada uno de los archivos externos, también lo quitamos del programa principal, para que no esté por duplicado

 por último, en la parte inicial del programa principal, dentro del preprocesador, incluimos todos los archivos de las funciones, para que más adelante puedan ser utilizadas 

agenda.c
/* Programa agendapor Jose Vicente Carratala */#define NOMBREPROGRAMA «Programa agenda»#define VERSION «1.0»#define AUTOR «Jose Vicente Carratala»#include <stdio.h>#include <string.h>#include «libAgenda/datos.c»#include «libAgenda/listadoRegistros.c»#include «libAgenda/introducirRegistro.c»#include «libAgenda/eliminarRegistro.c»#include «libAgenda/buscarRegistro.c»#include «libAgenda/actualizarRegistro.c»#include «libAgenda/menuPrincipal.c»
int main(int argc, char *argv[]) {
    // Primer registro        strcpy(agenda[1].nombre,»Jose Vicente»);    strcpy(agenda[1].telefono,»12345678″);    strcpy(agenda[1].email,»info@josevicentecarratala.com»);        // Segundo registro    strcpy(agenda[2].nombre,»Juan»);    strcpy(agenda[2].telefono,»53254″);    strcpy(agenda[2].email,»juan@josevicentecarratala.com»);    // Tercer registro    strcpy(agenda[3].nombre,»Jaime»);    strcpy(agenda[3].telefono,»64566″);    strcpy(agenda[3].email,»jaime@josevicentecarratala.com»);    menuPrincipal(); return 0;}

libAgenda/introducirRegistro.c
void introducirRegistro(){    printf(«Vamos a introducir un registro»);}

libAgenda/actualizarRegistro.c
void actualizarRegistro(){    printf(«Vamos a actualizar un registro»);}
libAgenda/datos.c
struct RegistroAgenda{        char nombre[50];        char telefono[50];        char email[50];    };        struct RegistroAgenda agenda[100];

libAgenda/listadoRegistros.c
void listadoRegistros(){    for(int i = 1; i<=3;i++){        printf(«Registro: %s %s %s \n»,agenda[i].nombre,agenda[i].telefono,agenda[i].email);    }}

libAgenda/eliminarRegistro.c
void eliminarRegistro(){    printf(«Vamos a eliminar un registro»);}

libAgenda/buscarRegistro.c
void buscarRegistro(){    printf(«Vamos a buscar un registro»);}
libAgenda/menuPrincipal.c
void menuPrincipal(){     // Mensaje de bienvenida printf(«%s v%s \n»,NOMBREPROGRAMA,VERSION); printf(«%s \n»,AUTOR); printf(«\t 1 – Listado de registros \n»); printf(«\t 2 – Introducir un registro \n»); printf(«\t 3 – Eliminar un registro \n»); printf(«\t 4 – Buscar un registro \n»); printf(«\t 5 – Actualizar un registro \n»); printf(«Tu opcion: «); char opcion = getchar(); printf(«La opción que has seleccionado es: %c \n»,opcion); switch(opcion){     case ‘1’:         listadoRegistros();         break;        case ‘2’:         introducirRegistro();         break;        case ‘3’:         eliminarRegistro();         break;        case ‘4’:         buscarRegistro();         break;     case ‘5’:         actualizarRegistro();         break;        default:            printf(«La opción que has introducido no es válida»);            break; }}

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *