0% encontró este documento útil (0 votos)
15 vistas16 páginas

Capitulo 03

El documento aborda el uso de funciones en el lenguaje de programación C, destacando su importancia para modularizar programas y facilitar la depuración. Se explican los componentes de una función, como su declaración, definición e invocación, así como el paso de argumentos y arreglos. También se menciona la recursividad y se proporcionan ejemplos prácticos para ilustrar estos conceptos.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
15 vistas16 páginas

Capitulo 03

El documento aborda el uso de funciones en el lenguaje de programación C, destacando su importancia para modularizar programas y facilitar la depuración. Se explican los componentes de una función, como su declaración, definición e invocación, así como el paso de argumentos y arreglos. También se menciona la recursividad y se proporcionan ejemplos prácticos para ilustrar estos conceptos.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Facultad de Ingeniería

Departamento de Electrónica y Automática

Unidad III
Funciones

Cátedra:
Computación II (ELO)
Informática II (BIO)
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO) Introducción al Lenguaje de Programación C

INDICE
3 Funciones .............................................................................................................................................. 1
3.1 Introducción.................................................................................................................................... 1
3.2 Definición de una función .............................................................................................................. 2
3.3 Paso de argumentos a una función ................................................................................................. 5
3.4 Paso de arreglos a funciones .......................................................................................................... 5
3.5 Recursividad................................................................................................................................. 10
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

Capítulo 3

3 Funciones

3.1 Introducción
El uso de funciones definidas por el programador permite dividir un programa grande en un
cierto número de componentes más pequeños, cada uno de las cuales posee un propósito
único e identificable. Por lo tanto, un programa en C se puede modularizar mediante el uso
inteligente de las funciones, y esto resulta útil para la depuración del programa, para evitar
redundancia de código, para generación de bibliotecas propias para posteriormente usarlas en
otros programas, etc.
Igualmente importante es la claridad lógica resultante de la descomposición de un programa en
varias funciones concisas, representando cada función alguna parte bien definida del problema
global. Estos programas son más fáciles de escribir y depurar, y su estructura lógica es más
fácil de entender que la de aquellos que adolecen de este tipo de estructuras. La mayoría de
los programas en C se modularizan de esta manera, aun cuando no impliquen la ejecución
repetida de las mismas tareas. De hecho, la descomposición de un programa en módulos
individuales y perfectamente definidos es la parte básica de las técnicas de análisis y diseño de
programas, tal como se vió al estudiar la metodología Top/Down.
El análisis Top/Down de un problema permite obtener sus bloques constitutivos de forma
simple y eficiente, a la vez que proporciona sólidas guías para encarar la modularización de un
programa por medio de funciones. Como ya se vió en la asignatura Computación I (ítem 6.10
Subprogramas), el algoritmo solución de cada uno de los bloques encontrados es factible de
ser encerrado en una “cápsula” que lo aisla del resto de los módulos, que le provee los
recursos necesarios para llevar a cabo su tarea y que define las vías de comunicación que la
cápsula tendrá con el exterior. Y cada uno de estos “algoritmos encapsulados” no es más que
lo que se denomina función.
Así, una función es un segmento de programa que realiza determinadas tareas bien definidas,
y todo programa en C consta de una o más funciones. Una de estas funciones se debe llamar
main. La ejecución del programa siempre comenzará por las instrucciones contenidas en main.
El uso de funciones dentro de un programa en C consta de tres partes perfectamente definidas:
1. Declaración de la función: Con esta estructura le indicamos al compilador que existe una
función definida por el usuario. Normalmente a la declaración de una función se la
denomina prototipo de función.
2. Definición de la función: Esta parte contiene el código ejecutable de la misma. Aquí es
donde se define la tarea (qué y cómo) que realizará la función.
3. Invocación o llamada de una función: Es cuando se la función se pone a trabajar.
Si un programa contiene varias funciones, sus definiciones pueden aparecer en cualquier
orden, pero deben ser independientes unas de otras. Esto es, una definición de una función no
puede estar incluida en otra. Cuando se accede a una función desde alguna determinada parte
del programa (o sea, cuando se invoca la función), se ejecutan todas las instrucciones de que
consta. Se puede acceder a una función desde distintos lugares de un programa.

1
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

Una vez que se ha completado la ejecución de una función, se devuelve el control al punto
desde el que se accedió a ella. Generalmente, una función procesará la información que le es
pasada desde el punto del programa en donde se accede a ella y devolverá un solo valor.
La información se le pasa a la función mediante identificadores especiales denominados
argumentos (también llamados parámetros) y es devuelta por la oración return. Sin embargo,
algunas funciones aceptan información pero no devuelven nada (void), mientras que otras
devuelven un valor a través de la instrucción return.

3.2 Definición de una función


La definición de una función tiene los siguientes componentes principales: la primera línea, la
declaración de variables locales y el cuerpo de la función. En términos generales, la definición
de una función se puede escribir de la siguiente manera:
tipo nombre_funcion(tipo1 arg1, tipo2, arg2, .., tipon argn) {
definiciones de variables locales;
oraciones;
}

Un ejemplo de función sería:


int suma( int a, int b) { // definición de una función
int s;
s = a + b;
return s;
}
La primera línea de la definición contiene la especificación del tipo de valor devuelto por la
función, seguido del nombre de la función y (opcionalmente) un conjunto de argumentos (tipo y
nombre), denominados argumentos formales, separados por comas y encerrados entre
paréntesis. La especificación del tipo de dato devuelto por la función se puede omitir si la
misma devuelve un entero o un caracter. Deben seguir al nombre de la función un par de
paréntesis vacíos si la definición de la función no incluye ningún argumento.
La llamada o invocación de la función del ejemplo anterior se realiza escribiendo en una
oración de asignación, el nombre de la función y la lista de parámetros separados por coma,
como se muestra a continuación:
int main() {

int x=5, y=7, sum;
sum = suma(x , y); // Invocación de la función

}
Los parámetros o argumentos de una función se clasifican en parámetros actuales y formales.
Los parámetros formales son los que están declarados en el prototipo de la función, son los
parámetros que reciben la información cada vez que se invoca la función. Los argumentos
formales permiten que se transfiera información desde el punto del programa en donde se
llama a la función. Las variables utilizadas como argumentos formales son locales en el
sentido de que no son reconocidos fuera de la función. En la definición de la función suma, a y
b son parámetros formales.
Los argumentos que están presentes en la invocación de la función se denominan
argumentos actuales o reales; representan la información real sobre la que operará la
función. En el ejemplo anterior, x e y son parámetros actuales.
2
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

Los parámetros actuales presentes en la invocación de una función, se corresponden en


número, orden y tipo con los parámetros formales de la declaración de la función. Cada
argumento formal debe tener el mismo tipo de datos que el correspondiente argumento actual.
Esto es, cada argumento formal debe ser del mismo tipo que el dato que recibe desde el punto
de llamada.
El resto de la definición de la función (el cuerpo) esta constituida por la definición de las
variables locales a la función y una oración compuesta que define las acciones que debe
realizar.
Se devuelve información desde la función hasta el punto del programa desde donde se llamó
mediante la instrucción return.
La instrucción return se puede escribir de la siguiente forma: return(expresión);
o simplemente return expresión;
No es obligatorio que la oración return lleve una expresión asociada. Tampoco es necesario
que aparezca en el cuerpo de la función, en este caso la función no retorna valor, return solo
puede tener como argumento una único variable de cualquier tipo de dato. El valor de retorno
debe ser del mismo tipo que el especificado en la definición de la función. Si no es del mismo
tipo el compilador C intentará aplicar alguna regla de conversión de tipo, si esto fracasa dará un
mensaje de error.
En C se puede omitir la especificación del tipo de función, que aparece en la primera línea de la
definición de la misma, en este caso la función devuelve una cantidad entera o caracter. Si se
desea que la función devuelva un tipo de datos diferente entonces la primera línea de la
función debe incluir la correspondiente especificación de tipo.
El siguiente ejemplo muestra la forma en que se declara una función que devuelve un valor
long. Se debe calcular el factorial de un entero positivo dado n, esta tarea es realizada por una
función llamada factorial a la cual se le debe entregar como argumento el número del cual se
desea calcular el factorial.
#include <stdio.h>
long int factorial(unsigned int n); /* declaración de la función */
int main( ) {
unsigned int n; /* numero n */
long int fact; /* factorial */
printf( "Introducir el número: " );
scanf( "%u", &n );
/*invocación de la función con los parámetros actuales */
fact = factorial( n );
printf( "Su factorial es: %d\n", fact );
return 0; /* valor genérico entregado por main */
}
long int factorial(unsigned int n) { /* primer línea de la definición */
/* declaración de variables locales*/
int i;
long int prod=1;
/*Cuerpo de la función*/
if(n > 1) {
for(i = 2, i <= n , ++i)
prod *= i;
}
3
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

return prod;
}
Si el tipo de datos especificado en la primera línea de la definición de la función es
inconsistente con la expresión de retorno, el compilador intentará convertir la cantidad
representada por la expresión al tipo de datos especificado en la primera línea. El resultado de
esto puede ser un error de compilación o una pérdida precisión (por ejemplo, debido al
truncamiento). En cualquier caso se deben evitar incongruencias de este tipo.
La siguiente definición de función es idéntica a la del ejemplo anterior, excepto en la primera
línea, en la que no aparece el especificador de tipo.
factorial(unsigned int n) {
int i;
long int prod=1;
if(n>1) {
for(i=2,i<=n,++i)
prod *=i;
}
return prod;
}

La función devolverá una cantidad entera, ya que no hay una declaración explícita del tipo de
datos en la primera línea de la definición. Sin embargo, la cantidad que se va a devolver (prod)
se declara como un entero largo. El resultado de esto puede ser un error. Se puede evitar el
problema añadiendo una declaración de tipo long int en la primera línea de la definición de la
función como en el ejemplo anterior.
La mayoría de los compiladores C permiten que aparezca la palabra clave void como
especificador de tipo de datos cuando se define una función que no devuelve nada. Es una
buena práctica de programación utilizar void cuando se dispone de ella.
El siguiente ejemplo calcula el máximo de dos cantidades enteras:
void maximo(int x , int y) {
/* Definición de una función que no retorna valor */
int z;
z = ( x >= y) ? x : y;
printf("\n\nValor maximo = %d",z);
}

El lenguaje C no permite definir una función dentro de otra. Si se invoca una función desde
una parte del programa anterior a la definición de tal función, debe entonces aparecer en el
módulo invocante la declaración de la función invocada (declaración por adelanto o declaración
forward), esto informará al compilador que la función será llamada antes de que sea definida.
Como ejemplo consideremos la función que convierte caracteres de minúscula a mayúscula.
# include <stdio.h>
/* declaración de la función */
char minusc_a_mayusc(char minusc);
int main( ) {
char minusc, mayusc;
printf("Introducir una letra minúscula: ");
scanf("%c", &minusc);
/* llamada a la función, con parámetros actuales */
mayusc = minusc_a_mayusc(minusc);

4
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

printf("La mayúscula equivalente es %c\n", mayusc);


return 0; /* valor genérico entregado por main */
}
// definición de la función
char minusc_a_mayusc(char c1) {
char c2;
c2 = ((c1 >= 'a') && (c1 <= 'z')) ? ('A'+ c1 - 'a') : c1;
return c2;
}

3.3 Paso de argumentos a una función


Cuando a una función se le envía un valor simple o primitivo mediante un argumento actual, se
realiza una copia del valor del argumento actual en el argumento formal de la función. Por lo
tanto se puede modificar el valor del argumento formal dentro de la función, pero el valor del
argumento actual en el punto de la llamada no cambiará. Este procedimiento para pasar el
valor de un argumento a una función se denomina paso de parámetros por valor. Veamos un
ejemplo sencillo para ejemplificar esto:
#include <stdio.h>
int modificar (int a);
int main( ) {
int a = 2;
printf("El valor de a antes del llamado es %d\n",a);
modificar(a);
printf("El valor de a después del llamado es %d\n",a);
return 0; /* valor genérico entregado por main */
}
int modificar(int a) {
a *= 3;
printf("\tEl valor de a dentro de la función es %d\n”, a);
}
Al ejecutar este programa se genera la siguiente salida:
El valor de a antes del llamado es 2
El valor de a dentro de la función es 6
El valor de a después del llamado es 2
En conclusión, en el lenguaje C todos los argumentos siempre se pasan por "por valor". De
esta manera en C la función llamada no puede alterar el valor de una variable de la función
llamadora, y únicamente puede cambiar el valor de la copia privada de esa variable. Como
veremos posteriormente, modificar el valor de un argumento actual dentro de una función solo
es posible en algunos casos especiales.

3.4 Paso de arreglos a funciones


El nombre de un arreglo se puede utilizar como argumento de una función, permitiendo así que
el arreglo completo sea pasado a la función.
Para usar un arreglo como argumento de una función, el nombre del arreglo debe aparecer
solo, sin corchetes o índices, como un argumento actual dentro de la llamada a la función.
El correspondiente argumento formal se escribe de la misma manera, pero debe ser declarado
como un arreglo dentro de la declaración de argumentos formales. Cuando se declara un
5
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

arreglo unidimensional como un argumento formal, el arreglo se escribe con un par de


corchetes vacíos. El tamaño del arreglo no se especifica dentro de la declaración de
argumentos formales.
El siguiente ejemplo ilustra el paso de arreglos a funciones,
float media(int cantidad, float datos[]); /* Declaración de funcion */
int main( ){
int n; /* Declaración de variable */
float med; /* Declaración de variable */
float lista[100]; /* Definición de arreglo */
...
med = media(n, lista); /* Invocacion a la funcion */
....
}
/* Definición de funcion */
float media(int a, float x[]){
.....
}
Dentro de main se ve una llamada a la función media. Esta llamada a función contiene dos
argumentos actuales: la variable entera n y el arreglo unidimensional en coma flotante lista.
Notar que lista aparece como una variable ordinaria dentro de la llamada a función.
En la primera línea de la función tenemos dos argumentos formales, llamados a y x. La
declaración formal de argumentos establece que a es una variable entera y x un arreglo
unidimensional en coma flotante. Así hay una correspondencia entre el argumento actual n y el
argumento formal a. Similarmente hay una correspondencia entre el argumento actual lista y
el argumento formal x. Notar que el tamaño de x no se especifica dentro de la declaración
formal de argumentos.
Se precisa tener algún cuidado cuando se escriben declaraciones de funciones que incluyen
especificaciones de tipo de argumentos. Si alguno de los argumentos es un arreglo, el tipo de
datos de cada arreglo debe estar seguido por un par de corchetes, indicando así que el
argumento es un arreglo. En el caso de prototipos de funciones, un par de corchetes vacíos debe
seguir al nombre de cada argumento arreglo. Similarmente, si la primera línea de una definición
de función incluye declaraciones formales de argumentos, cada nombre de arreglo que aparezca
como argumento formal debe estar seguido por un par de corchetes vacíos.
Hemos discutido que los argumentos son pasados a la función por valor cuando los argumentos
son variables ordinarias. Sin embargo, cuando se pasa un arreglo a una función, los valores de los
elementos del arreglo no son enviados a la función. En su lugar, el nombre del arreglo se
interpreta como la dirección del primer elemento del arreglo (la dirección de memoria
conteniendo el primer elemento del arreglo). Esta dirección se asigna al correspondiente
argumento formal cuando se llama a la función. El argumento formal se convierte por tanto en un
puntero al primer elemento del arreglo. Los argumentos pasados de esta manera se dice que son
pasados por referencia en vez de por valor.
Cuando se hace una referencia a un elemento del arreglo dentro de la función, el índice del
elemento se añade al valor del puntero para indicar la dirección del elemento especificado. Por
tanto, cualquier elemento del arreglo puede ser accedido dentro de la función. Además, si un
elemento del arreglo es alterado dentro de la función, esta alteración será reconocida en la
porción del programa desde la que ha sido llamada (en realidad, en todo el ámbito de la definición
del arreglo).
6
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

El siguiente ejemplo pasa un arreglo de tres elementos enteros a una función donde estos
elementos son alterados. Los valores de los elementos del arreglo se escriben en tres partes del
programa para ilustrar los efectos de las alteraciones.
#include <stdio.h>
void modificar(int datos[]); /* Declaración de la función */
int main( ) {
int cont, a[3]; /* Definición del arreglo */
printf("Desde main, antes de llamar a la función:\n");
for(cont = 0; cont <= 2; ++cont) {
a[cont] = cont + 1;
printf("a[%d] = %d\n", cont, a[cont]);
}
modificar(a);
printf("Desde main, después de llamar a la función: \n");
for(cont = 0; cont <= 2; ++cont)
printf("a[%d] = %d\n", cont, a[cont]);
}
/* definición de función */
void modificar(int a[]) {
int cont;
printf("Desde la función, después de modificar los valores:\n");
for(cont = 0; cont <= 2; ++cont) {
a[cont] = -9;
printf("a[%d] = %d\n", cont, a[cont]);
}
}

Los elementos del arreglo tienen asignados los valores a[0] = 1, a[1] = 2, a[2] = 3 en
el primer bucle de main. Estos valores se muestran después de ser asignados. Entonces el
arreglo se pasa a la función modificar, donde se les asigna el valor -9 a cada elemento del
arreglo. Estos valores se muestran dentro de la función. Finalmente, los valores del arreglo se
muestran de nuevo desde main, una vez que el control ha sido transferido desde modificar
hasta main.
El hecho de que un arreglo pueda ser modificado globalmente dentro de una función
proporciona un mecanismo adecuado para mover múltiples datos de o desde una función a la
porción llamante del programa. Simplemente se pasa el arreglo a la función y se alteran sus
elementos dentro de ella. O, si el arreglo debe ser preservado, se copia el arreglo (elemento
por elemento) dentro de la porción llamante del programa, se pasa la copia a la función y se
realizan las alteraciones. El programador debe tener cierto cuidado al alterar un arreglo dentro
de una función pues es muy fácil alterarlo de modo no intencionado fuera de la función.
Consideremos el problema de ordenar una lista de n enteros en una secuencia de valores
algebraicos crecientes. Escribamos un programa que realice la reordenación de modo que no
se use un almacenamiento innecesario. Por tanto, el programa contendrá solo un arreglo
unidimensional de enteros llamado x, en el que se reordenará un elemento cada vez. La
reordenación comenzará recorriendo todo el arreglo para encontrar el menor de los números
(algebraicamente). Este número será intercambiado con el primer elemento del arreglo,
colocando así el menor elemento al principio de la lista. El resto de los n-1 elementos del
arreglo se recorrerán para encontrar el menor, que se intercambiara con el segundo y así
sucesivamente, hasta que el arreglo completo sea reordenado. La reordenación completa
7
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

requerirá de n-1 pasos a lo largo del arreglo, pero cada vez la parte del arreglo a considerar es
menor.
Para encontrar el menor de los números en cada paso, comparamos secuencialmente cada
número del arreglo, x[i], con el número de comienzo, x[elem], donde elem es una variable
entera que se usa para identificar un elemento particular del arreglo. Si x[i] es menor que
x[elem], entonces se intercambian los dos números; en otro caso se dejan los dos números
en sus posiciones originales.
El programa en C que ejecuta esta tarea se escribe de la siguiente manera:
#include <stdio.h>
#define TAM 100
void reordenar(int cantidad, int datos[]); /* Declaración de la función */
int main( ) {
int i, n, x[TAM];
/* Leer un valor para n */
printf("Cuántos números serán introducidos? ");
scanf("%d",&n);
fflush(stdin); /* Esta función limpia el buffer de teclado */
printf("\n");
/* Leer la lista de números */
for(i = 0; i < n; ++i) {
printf("i = %d x = ", i + 1);
scanf("%d", &x[i]);
}
/* reordenar todos los elementos del arreglo */
reordenar(n,x);
/* mostrar el arreglo reordenado */
printf("Lista de números reordenada:\n");
for(i = 0; i < n; ++i)
printf("i = %d x = %d\n", i + 1, x[i]);
return 0;
}
void reordenar(int n, int x[]) {
int i, elem, temp;
for (elem = 0; elem < n-1; ++elem) {
/* encontrar el menor del resto de los elementos */
for (i = elem + 1; i < n; ++i) {
if ( x[i] < x[elem]) {
/* intercambiar los dos elementos */
temp = x[elem];
x[elem] = x[i];
x[i] = temp;
}
}
}
}
Sin embargo, se requiere algún cuidado cuando se pasan arreglos multidimensionales a una
función. En particular, las declaraciones de argumentos formales dentro de la definición de función
debe incluir especificaciones explícitas de tamaño en todos los índices excepto en el primero.
8
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

Estas especificaciones deben ser consistentes con las correspondientes especificaciones de


tamaño en el programa llamante. El primer índice puede ser escrito como un par de corchetes
vacíos, como en un arreglo unidimensional. Los prototipos correspondientes de función deben
escribirse de la misma manera.
El siguiente programa ilustra los conceptos antes mencionados.
Supóngase que se desea leer dos matrices de números enteros y calcular su suma, esto es,
c[i][j] = a[i][j] + b[i][j]

Para lograr esto supondremos que las matrices tienen igual número de filas y columnas y que
no exceden de 20 filas y 20 columnas.
El programa se modulariza escribiendo funciones separadas para leer los arreglos, calcular la
suma de los elementos del arreglo y escribir el arreglo. Llamaremos a estas funciones
leematriz, sumamatriz, escribirmatriz.
#include <stdio.h>
#define MAXF 20
#define MAXC 20
/* prototipos de función */
void leematriz (int a[][MAXC], int nfilas, int ncols);
void sumamatriz(int a[][MAXC], int b[][MAXC], int c[][MAXC],int nfilas, int ncols);
void escribirmatriz (int c[][MAXCOL], int nfilas, int ncols);

int main( ) {
int nfilas, ncols; /* número de filas y número de columnas */
/* definiciones de arreglos */
int a[MAXF][MAXC], b[MAXF][MAXC], c[MAXF][MAXC];

printf("Cuantas filas?");
scanf("%d", &nfilas);
printf("Cuántas columnas?");
scanf("%d", &ncols);
printf("Primera Matriz:\n");
leematriz(a, nfilas, ncols);
printf("Segunda Matriz:\n");
leematriz(b, nfilas, ncols);
sumamatriz(a, b, c, nfilas, ncols);
printf("Suma de los Elementos:\n");
escribirmatriz(c, nfilas, ncols);
return 0;
}
/* función para leer una matriz */
void leermatriz(int a[][MAXC], int m, int n) {
int fila, col; /* contador de número de filas y columnas */
for(fila = 0; fila < m; ++fila) {
printf("Introd. datos para la fila nro. %2d\n", fila +1);
for(col = 0; col < n; ++col) {
scanf("%d", &a[fila][col]);
}
}
}

9
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

/* función para sumar dos matrices */


void sumamatriz(int a[][MAXC], int b[][MAXC, int c[][MAXC], int m, int n) {
int fila, col;
for(fila = 0; fila < m; ++fila)
for(col = 0; col < n; ++col)
c[fila][col] = a[fila][col] + b[fila][col];
}
/* función para escribir una matriz en la salida estándar */
void escribirmatriz(int a[][MAXC], int m, int n) {
int fila, col;
for(fila = 0; fila < m; ++fila) {
for(col = 0; col < n; ++col) {
printf("%4d", a[fila][col]);
}
printf("\n");
}
}
Observar la escritura de las declaraciones formales de argumentos dentro de cada definición
de función.

3.5 Recursividad
Se llama recursividad a un proceso mediante el cual una función se invoca repetidamente a sí
misma hasta que se satisface alguna condición de finalización predefinida. Este proceso se
utiliza para “simular” una repetición sin emplear ninguna de las estructuras de control de flujo
iterativas conocidas.
Se deben satisfacer dos condiciones para poder resolver un problema recursivamente:
Cond. 1). El algoritmo solución se debe poder escribir en forma recursiva.
Cond. 2). El enunciado del problema debe contener una condición de fin asegurada.
Para entender como trabaja el proceso de recursividad se debe conocer previamente la
operación del mecanismo de invocación de funciones en el lenguaje C, aunque es
prácticamente lo mismo para cualquier lenguaje de programación que emplee subprogramas.
Cuando un programa A invoca a un subprograma B, el programa A detiene su ejecución y
comienza la ejecución del subprograma B. Cuando este último finaliza, el programa A retoma la
ejecución a partir del punto donde la detuvo, tal como lo muestra el siguiente esquema:
Programa A
A1

A2… LLAMADOR

A2 suspende su trabajo y comienza B.


Invocación

A espera que B
termine.
Subprograma B

B finaliza su trabajo y A2 continúa


desde donde dejó.

…A2

10
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

Resulta claro que, dado que el programa A y el subprograma B residen en memoria, todo este
proceso debe contar con algún medio de almacenar la dirección de memoria donde el
programa A dejó pendiente su ejecución para así poder retomarla cuando finalice el
subprograma B.
Este almacenamiento está dado por una zona especial en la memoria de la computadora que
se denomina stack (o también pila o LIFO) debido a la forma en la que almacena y devuelve
los valores guardados en ella. Quizás el nombre LIFO, por Last In – First Out (“El ultimo en
entrar es el primero en salir”) es el más representativo de la forma de operación de esta zona
de memoria. Para entenderlo mas claramente se debe pensar en la forma de trabajo de una
pila de platos, en la que el ultimo plato ingresado a la pila (el que se encuentra en lo mas alto
de la pila) es el primero en ser retirado cuando es necesario usarlo.
El lenguaje C almacena en esta “pila” o stack tanto las direcciones de retorno (direcciones de
memoria desde donde deben continuar las ejecuciones de los programas o subprogramas
luego de la invocación de otro subprograma) como las variables locales del subprograma,
siendo estas las verdaderas variables locales y/o los parámetros formales del subprograma o
función.
Mediante un ejemplo se mostrará como se genera la repetición recursiva empleando el stack
como medio de almacenamiento temporario de la información. El problema a resolver es:
“Desarrollar un programa que lea una línea de texto caracter por caracter y luego escriba los
caracteres en orden inverso al ingresado”.
#include <stdio.h>
#define EOLN '\r'
void inverso(void);
void main(void) {
printf("Introduzca una línea de texto debajo\n");
inverso(); /* Primera invocación */
}
void inverso(void) {
char c;
if((c=getchar())!= EOLN)
inverso(); /* Invocacion recursiva */
putchar( c );
}
Los valores a ingresar a este programa será la siguiente secuencia de letras y símbolos:

A B C <Enter>
1- Ante la primer invocación de la función inverso() en el
Variable
main, se almacena la posición de retorno en el stack, se crea la c=A
variable local c y esta recibe el valor ‘A’ al ejecutarse la función
getchar. Dirección de retorno
1º invocación
Dado que el valor leído no es EOLN, la función inverso() se
STACK
auto-invoca.

11
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

Variable
c=B
2- Ante la segunda invocación de la función inverso(), se
almacena la posición de retorno en el stack, se crea una nueva Dirección de retorno
2º invocación
variable local c y esta recibe el valor ‘B’ al ejecutarse la función
getchar. Variable
c=A
Dado que el valor leído no es EOLN, la función inverso() se
auto-invoca nuevamente. Dirección de retorno
1º invocación

STACK

Variable
c=C

Dirección de retorno
3- Ante la tercera invocación de la función inverso(), se 3º invocación
almacena la posición de retorno en el stack, se crea una nueva Variable
variable local c y esta recibe el valor ‘C’ al ejecutarse la función c=B
getchar.
Dirección de retorno
Dado que el valor leído no es EOLN, la función inverso() se 2º invocación

auto-invoca otra vez. Variable


c=A

Dirección de retorno
1º invocación

STACK

Variable
c = \n

Dirección de retorno
4º invocación
4- Ante la cuarta invocación de la función inverso(), se almacena
Variable
la posición de retorno en el stack, se crea una nueva variable local c c=C
que recibe el valor ‘\n’ al ejecutarse la función getchar.
Dirección de retorno
Dado que ahora el valor leído es EOLN, la función inverso() NO 3º invocación
se auto-invoca y procede a ejecutar la función putchar( c )
Variable
mostrando por pantalla una nueva línea en blanco. c=B

Luego de esto se produce el retorno de la función inverso(), Dirección de retorno


destruyéndose la última variable c creada y extrayendo la dirección 2º invocación
de retorno de la 4º invocación. Variable
c=A

Dirección de retorno
1º invocación

STACK

12
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

Variable
c=C

Dirección de retorno
5- Luego del retorno de la 4º invocación se produce la ejecución de 3º invocación

la función putchar( c ) de la tercera invocación mostrando por Variable


pantalla la letra ‘C’. c=B

A continuación se produce el retorno de la función inverso(), Dirección de retorno


2º invocación
destruyéndose la variable c creada y extrayendo la dirección de
retorno de la 3º invocación. Variable
c=A

Dirección de retorno
1º invocación

STACK

Variable
c=B
6- Luego del retorno de la 3º invocación se produce la ejecución de
la función putchar( c ) de la segunda invocación mostrando por Dirección de retorno
pantalla la letra ‘B’. 2º invocación

A continuación se produce el nuevo retorno de la función Variable


c=A
inverso(), destruyéndose la variable c creada y extrayendo la
dirección de retorno de la 2º invocación. Dirección de retorno
1º invocación

STACK
7- Luego del retorno de la 2º invocación se produce la ejecución de
la función putchar( c ) de la primer invocación mostrando por Variable
pantalla la letra ‘A’. c=A

A continuación se produce el ultimo retorno de la función Dirección de retorno


1º invocación
inverso() al main, destruyéndose la primer variable c creada y
extrayendo la dirección de retorno de la 1º invocación, quedando STACK

vacío el stack.

Analizando el crecimiento del stack a lo largo del algoritmo, es fácil determinar que el consumo
de memoria requerido para esta solución recursiva excede largamente al de una solución
equivalente que empleara un arreglo de char y una simple iteración para lograr la impresión de
valores en secuencia inversa.
De hecho, es importante resaltar que una alternativa recursiva puede no ser la solución
óptima a un problema, aún cuando éste sea recursivo por naturaleza. Una solución con
iteraciones convencionales puede ser más eficiente en cuanto a utilización de recursos
(típicamente la memoria) y rapidez de ejecución, y por ello, el uso de la recursividad puede
involucrar un compromiso entre simplicidad y prestaciones, por lo que cada problema debe
juzgarse individualmente teniendo en cuenta sus características específicas.
Otro ejemplo de función recursiva es el cálculo del factorial de un número, definido
recursivamente en forma matemática (condición 1) como:

y cuya solución algorítmica recursiva es la siguiente:

13
COMPUTACIÓN II (ELO) - INFORMÁTICA II (BIO)

long factorial( long n ) {


if( n <= 1 ) {
return 1;
} else {
return ( n * factorial( n-1 ) );
}
}
Este nuevo ejemplo de solución recursiva tiene un comportamiento análogo al anterior, excepto
que ahora, la variable que se crea en el stack en cada invocación es el parámetro formal n.

14

También podría gustarte