E. SUBPROGRAMAS. FUNCIONES EN C



- Concepto de subprograma. Funciones y procedimientos
- Definición, declaración y uso de funciones
- Paso de parámetros: por valor y por referencia
- Paso de arrays a funciones
- Ámbito y tipo de almacenamiento de variables
- Idea de recursividad


CONCEPTO DE SUBPROGRAMA. FUNCIONES Y PROCEDIMIENTOS
Un subprograma es una parte de un programa con un nombre y ámbito propio, que se invoca por su nombre y con los parámetros adecuados.


DEFINICIÓN, DECLARACIÓN Y USO DE FUNCIONES
- El lenguaje C se basa en el uso de funciones. No se puede escribir ninguna línea de código ejecutable que no pertenezca a una función.

- El lenguaje C no dispone de procedimientos, sólo permite el uso de funciones. Para emular el uso de procedimientos se utilizan funciones que devuelvan como valor de retorno un dato de tipo void (lo que es igual a no devolver ningún dato).

- No se puede definir una función dentro de otra función. Todas las funciones deben estar en el mismo nivel.

- Siempre debe existir una función denominada main dentro del código del programa. Esta función será la que se ejecutará cuando se arranque el programa.

# include <stdio.h>
float suma (int, int); 1.Prototipo de función
int main ()
{
int a, b;
float z;
printf (“Dame dos enteros:”);
scanf (“%d %d”, &a, &b);
z= suma(a, b); 2. Llamada a la función
printf (“La suma vale %f \n", z);

return o;
float suma (int x, int y)
{
float w;
w= x+y; 3. Código de la función
return w;
}



PASO DE PARAMETROS POR VALOR Y POR REFERENCIA
  • Por valor: se hace una copia local de las variables que van como argumento de la función.
  • Por referencia: La función tiene acceso al argumento(s) del ámbito de llamada. En C se hace con punteros. Es decir, se pasa la dirección de memoria de argumento(s).


PASO DE ARRAYS A FUNCIONES: (El paso es por referencia)
  • Prototipo: Se explicita tipo, nombre y dimensiones. En C se puede omitir la primera dimensión (vectores y matrices).
float escalar (int n, float v1[DIM], float v2[DIM]);
  • Definición: Aquí es necesario nombre y dimensiones. En C se puede omitir la primera dimensión (vectores y matrices).
float escalar (int n, float v1[DIM], float v2[DIM])
{
float prod = 0
for (i = 0, i < n, i ++)
prod + = v1[i]*v2[i];
return prod;
}
  • Llamada: Se llama poniendo como argumento el nombre del array.
int main ()
{
int k;
float x[DIM], y[DIM], z;
…………..
…………..
…………..
z = escalar (x, y);
}
  • Punteros a funciones: Una función (su código) ocupa un espacio físico en memoria, por tanto es posible apuntar a ese espacio. El puntero correspondiente, definido en un prototipo idéntico al de la función que va a apuntar, adquiere la dirección de memoria correspondiente en una asignación a través del nombre de la función.


TIPO DE ALMACENAMIENTO DE VARIABLES

El lenguaje C define dos tipos de almacenamiento:
-Automático: se corresponde a las variables locales que se definen dentro de una función.
-Estático: variables locales o globales cuyo valor persiste entre las distintas inicializaciones de un objeto. Si son variables locales se distinguen de las automáticas por utilizar la palabra reservada static.
Las variables automáticas (las definidas dentro de una función sin utilizar la partícula static) se crean cuando se llama a la función y desaparecen cuando finaliza su ejecución, por tanto no conservan el valor de una llamada a otra. Las variables estáticas proporcionan almacenamiento permanente para la variable, de forma que no se destruye cuando finaliza la ejecución de la función en la que se define. Además, su valor perdura de una ejecución a otra. Las variables estáticas pueden ser inicializadas con un valor constante cuando son declaradas, pero esa inicialización sólo tiene lugar la primera vez que se llama al objeto dado por esa variable.
El ámbito de una variable define la visibilidad de la misma, es decir, desde dónde se puede acceder a dicha variable. En cuanto al ámbito, las variables pueden ser locales o globales.
Una variable local es la que se define dentro de una función. El ámbito de una variable local se restringe a la función en la que se define, por tanto no es visible desde fuera de la función. Las variables locales pueden ser automáticas o estáticas (según incluyan o no la partícula static). En el primer caso, las variables se crean cuando se llama a la función y se destruyen cuando de la función a otra.
Una variable global es la que se define fuera de las funciones, por tanto puede ser accedida desde cualquier función. Las variables globales son siempre estáticas.

Algunos errores a evitar son:
-Utilizar punto y coma en la definición de una función.
-Definir dos variables globales con el mismo nombre.
-Definir una variable global con el mismo nombre que una función.
-Definir dos funciones con el mismo nombre.
-Asumir un valor por defecto para las variables automáticas.


#include <stdio.h>
void imprimir ()
{
static int varl = 0;
int var2 = 0;
var1= var1+1;
var2= var2+1;
printf ( “var1 = %d, var2 = %d\n”, var1, var2);
}
int main (void)
{
int i;
for (i=0; i<3; i++) imprimir();
}

La salida de dicho problema es la siguiente:
var1=1, var2=1
var1=2, var2=1
var1=3, var2=1

Esto es así porque la variable var1, al ser estática, conserva el valor entre las diferentes llamadas a la función imprimir. En cambio, la variable var2, al ser automática, no conserva el valor entre las diferentes llamadas.


RECURSIVIDAD
Se llama recursividad a un proceso mediante el que la función se llama a sí misma de forma repetida, hasta que satisface alguna condición determinada.
Se deben cumplir dos condiciones para que un problema se pueda resolver recursivamente:
1.-El problema se debe escribir en forma recursiva
2.-La especificación del programa debe incluir una función de fin.
Por ejemplo:
Supongamos que deseamos calcular el factorial de una cantidad entera positiva. Normalmente expresaríamos el problema así: n!= 1 x 2 x 3 x… x n (donde “n” es el entero positivo dado).
Sin embargo, podemos también expresar el problema como: n!= n x (n-1)! Siendo esta forma un forma recursiva.
También sabemos que 1!= 1 por definición. Esta última condición nos proporciona una condición de fin para el proceso recursivo.