Diferencia entre revisiones de «Programación en C/Uso de funciones»

Contenido eliminado Contenido añadido
asdfghkñúre
Etiqueta: posible vandalismo
Tegel (discusión | contribs.)
m Revertidos los cambios de 181.30.27.24 (disc.) a la última edición de Syum90
Línea 1:
<noinclude>
Pija por el culo
{{Navegar|anterior=Instrucciones de control
|siguiente=Vectores
|reducido=Si
}}
</noinclude>
 
==Funciones==
 
Como vimos
[[Programación en C/Primer programa en C#Diseccionando el "Hola Mundo"|anteriormente]]
C tiene como bloque básico la función <source enclose=none
lang=c>main()</source> , también hemos visto la sentencia <source enclose=none
lang=c>printf()</source> que es otra función, y de igual forma hay muchas más funciones predefinidas, pero
nosotros mismos también podemos definir nuestras propias funciones. De hecho, es
fundamental hacerlo.
 
<!--
TODO: reescribir esto con otro ejemplo.
-->
Podemos definir una función cualquiera de la misma manera en que definimos la
función <source enclose=none lang=c>main()</source>. Basta con poner su tipo,
su nombre, sus argumentos entre paréntesis y luego, entre llaves, su código:
 
<source lang=c>
/* Inclusión de archivos */
#include <stdio.h>
 
void holamundo(void) /* Función donde se ejecuta la lógica del programa */
{
printf("Hola Mundo\n"); /* imprime la cadena */
return; /* sale de la función */
}
int main(void) /* Función principal del programa */
{
holamundo(); /* llamada a la función holamundo */
return 0; /* sale del programa con código 0 (correcto) */
}
</source>
 
Este código es en todo equivalente al "Hola Mundo" original, sólo que nos
muestra cómo escribir y cómo utilizar una función. Y además nos muestra un
principio de buena programación: meter las sentencias que "hacen el trabajo" en
otras funciones específicas para sacarlas de <source enclose=none
lang=c>main()</source>, dejando en ésta tan sólo un guión general de lo que
hace el programa, no las órdenes específicas. De esta manera se facilita la
comprensión del programa, y por tanto el futuro trabajo de modificarlo.
 
===La sentencia <source enclose=none lang=c>return</source>===
 
La sentencia <source enclose=none lang=c>return</source> puede utilizarse dentro de una función para terminar su ejecución.
 
En el ejemplo anterior, la función <source enclose=none lang=c>holamundo</source> fue declarada con valor de retorno de tipo <source enclose=none lang=c>void</source> (es decir, valor de retorno nulo). En ese caso, la sentencia <source enclose=none lang=c>return</source> no lleva ningún parámetro adicional, ya que la función no debe devolver ningún valor a la función que la llama.
 
En cambio, la función <source enclose=none lang=c>main</source> tiene un valor de retorno de tipo <source enclose=none lang=c>int</source>, por lo que <source enclose=none lang=c>return</source> debe ir seguido de un valor entero (0 en el ejemplo). El valor 0 se utiliza para indicar que el programa ha llegado a un punto en el que todo se ha desarrollado correctamente y se utiliza cualquier otro valor para indicar que ha habido algún tipo de error.
 
La instrucción <source enclose=none lang=c>return</source> no es una función, se trata de una sentencia que lo que hace es retornar como valor de la función el valor que se le proporciona como argumento.
 
===Argumentos===
 
Las funciones también pueden recibir argumentos o parámetros, para modificar su comportamiento. Por ejemplo, la definición de una función para sumar dos números sería de la siguiente
manera:
 
===Declaración y definición===
 
En el ejemplo anterior podemos notar que la función <source enclose=none lang=c>sumar</source> figura en el código antes que <source enclose=none lang=c>main</source>. ¿Qué pasaría si las escribiéramos en distinto orden?
 
<source lang=c>
#include <stdio.h>
 
int main(void)
{
int suma = sumar(5, 3); /* ERROR, sumar no ha sido declarada aún */
printf("La suma es: %d ", suma);
return 0;
}
 
int sumar(int numero1, int numero2)
{
return numero1 + numero2;
}
</source>
 
En este caso el programa es erróneo y no compila, ya que en la línea donde se llama a la función <source enclose=none lang=c>sumar</source>, el compilador aún no conoce ninguna función con ese nombre, y cuáles son sus argumentos y valor de retorno.
 
Una posible solución es '''declarar''' el '''prototipo''' de la función al principio, para informar al compilador que existe, y luego '''definir''' el '''cuerpo''' de la misma en cualquier lugar del programa:
<source lang=c>
#include <stdio.h>
 
/* Declaración */
int sumar(int numero1, int numero2);
 
int main(void)
{
int suma = sumar(5, 3);
printf("La suma es: %d ", suma);
return 0;
}
 
/* Definición */
int sumar(int numero1, int numero2)
{
return numero1 + numero2;
}
</source>
 
===Paso de Parámetros===
 
Las funciones pueden recibir datos como lo hemos observado, pero existen dos formas de enviar los datos hacia una función '''por valor''' y '''por referencia''', las cuales modifican en diferente forma el comportamiento de el programa.
 
 
 
====Por Valor====
 
El paso por valor envía una copia de los parámetros a la función por lo tanto
los cambios que se hagan en ella no son tomados en cuenta dentro de la función
<source enclose=none lang=c>main()</source>. Ejemplo:
 
<source lang=c>
/*
* por_valor.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/
 
#include <stdio.h>
 
void sumar_valor(int numero); /* prototipo de la función */
 
int main(void)
{
int numero = 57; /* definimos numero con valor de 57*/
 
sumar_valor(numero); /* enviamos numero a la función */
 
printf("Valor de numero dentro de main() es: %d\n", numero);
/* podemos notar que el valor de numero se modifica
* sólo dentro de la función sumar_valor pero en la principal
* número sigue valiendo 57
*/
 
return 0;
}
 
void sumar_valor(int numero)
{
numero++; /* le sumamos 1 al numero */
 
/* el valor de número recibido se aumenta en 1
* y se modifica dentro de la función sumar_valor()
*/
printf("Valor de numero dentro sumar_valor() es: %d\n", numero);
 
return;
}
</source>
 
====Por Referencia====
 
El paso por referencia se hace utilizando apuntadores. Se envía la dirección de
memoria de la variable, por lo tanto los cambios que haga la función si afectan
el valor de la variable. Ejemplo:
 
<source lang=c>
/*
* por_referencia.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/
 
#include <stdio.h>
 
void sumar_referencia(int *numero); /* prototipo de la función */
 
 
int main(void)
{
int numero = 57; /* definimos numero con valor de 57*/
 
sumar_referencia(&numero); /* enviamos numero a la función */
 
printf("\nValor de numero dentro de main() es: %d ", numero);
/* podemos notar que el valor de numero se modifica
* y que ahora dentro de main() también se ha modificado
* aunque la función no haya retornado ningún valor.
*/
 
return 0;
}
 
void sumar_referencia(int *numero)
{
*numero += 1; /* le sumamos 1 al numero */
 
/* el valor de numero recibido se aumenta en 1
* y se modifica dentro de la función
*/
printf("\nValor de numero dentro sumar_referencia() es: %d", *numero);
 
return;
}
</source>
 
===Variables Locales y Globales===
 
Además de pasar valores a una función, también se pueden declarar tipos de
datos dentro de las funciones, estos tipos de datos declarados dentro de una
función solo son accesibles dentro de esta misma función y se les conocen como
variables locales, así pues podemos definir los mismos nombres de variables en
diferentes funciones, ya que estas variables solo son accesibles dentro de esas
funciones. Ejemplo:
 
<source lang=c>
/*
* locales.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/
 
#include <stdio.h>
 
void funcion1()
{
int dato = 53; /* definimos dato en 53*/
char num1 = 'a'; /* num1 vale a */
 
/* imprimimos */
printf("Funcion1, dato=%d, num1=%c\n", dato, num1);
 
return;
}
 
void funcion2()
{
int dato = 25; /* definimos dato en 25*/
char num2 = 'z'; /* num2 vale z*/
 
/* imprimimos */
printf("Funcion2, dato=%d, num2=%c\n", dato, num2);
 
return;
}
 
int main(void)
{
funcion1(); /* llamamos a funcion1() */
 
funcion2(); /* llamamos a funcion2() */
 
return 0;
}
</source>
 
En este caso la variable dato, esta definida dentro de cada una de las
funciones y son totalmente distinta una de otra y no se puede utilizar fuera de
esta, así pues num2 no puede ser utilizada por la funcion1() y num1 tampoco
puede ser utilizada por funcion2().
 
Existen pues variables que se definen fuera de la función principal main() y
fuera de cualquier otra función creada por nosotros, estas variables se les
conoce con el nombre de Variables Globales ya que se pueden utilizar dentro de
main() y dentro de cualquier función creada por nosotros. Ejemplo:
 
<source lang=c>
/*
* global.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/
 
#include <stdio.h>
 
int variable_global = 99; /* inicializamos la variable global */
 
void funcion();
 
int main(void)
{
/* imprimimos el valor*/
printf("main(), acceso a variable_global %d\n", variable_global);
 
/* llamamos a la función */
funcion();
 
return 0;
}
 
void funcion()
{
/* imprimimos el valor*/
printf("funcion(), acceso a variable_global %d\n", variable_global);
 
return;
}
</source>
 
==Funciones Recursivas==
 
La recursividad (recursión) es la propiedad por la cual una función se llama
a sí misma '''directa''' o '''indirectamente'''. La recursión
indirecta implica utilizar más de una función.
 
Se puede considerar la recursividad como una alternativa a la iteración.
La recursión permite especificar soluciones naturales, sencillas, que serían,
en caso contrario, difíciles de resolver. Toda función recursiva debe
contemplar un '''caso base''' o '''condición de salida''', para terminar, o la
recursividad no podrá terminar nunca.
 
Una función recursiva podría definirse así:
 
<source lang=c>
funcion_recursiva( /* parámetros recibidos por la función */ )
{
/* Código */
funcion_recursiva( ); /* llamada a la función misma */
/* Código */
}
</source>
 
Uno de los ejemplos más representativos en la recursividad es el factorial de
un numero ( n! ):
 
:<math> n!=\prod_{k=1}^n k \qquad \forall n \in \mathbb{N}\!</math>
 
la definición de recursividad del factorial es:
 
:<math> n! = \begin{cases}
1 & \text{ Si } n = 0 \\
n (n-1)! & \text{ Si } n > 0 \\
\end{cases}
\qquad \forall n \in \mathbb{N}.
</math>
 
En esta definición, n = 0, es nuestro caso base, que le da fin a la
recursividad.
 
Entonces nuestro programa que calcula el factorial es:
 
<source lang=c>
/*
*factorial.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/
 
#include <stdio.h>
 
long factorial(int n)
{
if (n == 0) /* caso base */
return 1; /* como 0! = 1, se retorna 1*/
else
return n * factorial (n - 1); /* llamada a esta misma función */
}
 
int main(void)
{
/* en este caso se llama a la función y se imprime directamente*/
printf("%ld ", factorial(5));
 
return 0;
}
</source>
 
También existen otros tipos de funciones recursivas como lo es el producto de
dos números. El producto de '''a <math>\times</math> b''', donde '''a''' y '''b''' son números
enteros positivos seria:
 
'''Solución iterativa:'''
 
<math> a \times b = \begin{matrix}\underbrace{a+a+\cdots+a} \\ \ b\ veces \end{matrix} = \sum_{i=1}^b a</math>
 
'''Solución recursiva:'''
 
<math> a \times b = \begin{cases}
0 & \text{ Si } b = 0 \\
a + a \times (b-1) & \text{ Si } b > 0 \\
\end{cases}
</math>
 
Así pues <math>7 \times 3</math> es:
 
:<math> 7 \times 3 = 7 + 7 \times 2 = 7 + 7 + 7 \times 1 = 7 + 7 + 7 + 0 =
21</math>
 
Podemos ver que la multiplicación de dos números a, b se puede transformar en
otro problema más pequeño multiplicar '''a''' por '''(b-1)''',
el caso base se produce cuando '''b = 0''' y el producto es '''0'''. Ejemplo:
 
<source lang=c>
/*
* producto.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/
 
#include <stdio.h>
 
int producto(int a, int b)
{
if (b == 0) /* caso base */
return 0; /* como b = 0, se retorna 0*/
else
return a + producto (a, b - 1); /* llamada a esta misma función */
}
 
int main(void)
{
/* en este caso se llama a la función y se imprime directamente*/
printf("%i ", producto( 7, 3));
 
return 0;
}
</source>
 
===Recursividad indirecta o recursión mutua===
 
Esta se produce cuando una función llama a otra, que esta a su vez terminará
llamando de nuevo a la primera función. El siguiente programa visualiza el
alfabeto utilizando recursión indirecta o mutua:
 
<source lang=c>
/*
* elalfabeto.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/
 
#include <stdio.h>
 
void funcionA(char c); /* se declara el prototipo de la función para que el llamado */
void funcionB(char c); /* a la misma en la función no sea implícita */
 
int main(void)
{
 
funcionA('z'); /* llamado a funcionA */
 
return 0;
}
 
void funcionA(char c)
{
if (c > 'a') /* caso base mientras c no sea menor que A */
funcionB(c); /* llamado a la funcionB */
 
printf("%c ", c); /* imprimimos el valor de c */
*la variable es un parametro no utilizado para este proceso
}
 
void funcionB(char c)
{
funcionA(--c); /* llamado a la funcionA decrementando el valor de 'z' */
}
</source>
 
===Recursión versus Iteración===
 
Tanto la iteración como la recursión se basan en estructura de control: la
iteración utiliza una estructura repetitiva y la recursión una estructura de
selección. La iteración utiliza explícitamente una estructura repetitiva
mientras que la recursión consigue la repetición mediante llamadas repetitivas
a funciones.
 
La iteración termina si la condición del bucle no se cumple, mientras que la
recursión termina cuando se reconoce un caso base.
 
La recursión puede presentar desventajas ante la iteración ya que se invoca
repetidas veces al mecanismo de llamada de funciones y se necesita un tiempo
mayor para realizar cada llamada.
 
La razón por la cual se puede elegir u optar por usar recursividad es que
existen muchos problemas complejos que poseen naturaleza recursiva y, en
consecuencia, son mas fáciles de implementar.
 
====Ejemplo Iterativo====
 
<source lang=c>
/*
* iterativo.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/
 
#include <stdio.h>
 
long factorial(int numero);
 
int main(int argc, char** argv)
{
int contador = 0;
 
/* calcula el factorial de 0 a 10 */
for ( contador = 0; contador <= 10; contador++ )
printf("%d! = %ld\n", contador, factorial( contador ));
 
return 0;
}
 
/* funcion factorial iterativa */
long factorial( int numero )
{
long resultado = 1;
int i = 0;
 
/* declaracion de la función factorial iterativa */
for ( i = numero; i >= 1; i-- )
resultado *= i;
 
return resultado;
}
</source>
 
====Ejemplo Recursivo====
 
<source lang=c>
/*
* recursivo.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/
 
#include <stdio.h>
 
long factorial(int numero);
 
int main(int argc, char** argv)
{
int contador = 0;
 
/* calcula el factorial de 0 a 10 */
for ( contador = 0; contador <= 10; contador++ )
printf("%d! = %ld\n", contador, factorial( contador ));
 
return 0;
}
 
/* función factorial recursiva */
long factorial( int numero )
{
if ( numero <= 0 ) /* caso base */
return 1; /* casos bases: 0! = 1 y 1! = 1 */
else /* llamada recursiva */
return numero * factorial( numero - 1 ); /* llamada a la función factorial */
}
</source>
 
<noinclude>
{{Navegar|anterior=Instrucciones de control
|siguiente=Vectores
|reducido=Si
}}
</noinclude>