|
Para
hacer que las instrucciones contenidas en una función, se ejecuten en
determinado momento, no es necesario más que escribir su nombre como una
linea de sentencia en mi programa. Convencionalmente en C los nombres de las
funciones se escriben en minúscula y siguen las reglas dadas anteriormente
para los de las variables, pero deben ser seguidos, para diferenciarlas de
aquellas por un par de paréntesis .
Dentro de estos paréntesis estarán ubicados los datos que se les pasan a las
funciones. Está permitido pasarles uno, ninguno ó una lista de ellos
separados por comas, por ejemplo: pow10( a ), getch(), strcmp( s1, s2 ) .
Un concepto sumamente importante es que los argumentos que se les envían a
las funciones son los VALORES de las variables y NO las variables mismas. En
otras palabras, cuando se invoca una función de la forma pow10( a ) en
realidad se está copiando en el "stack" de la memoria el valor que tiene en
ese momento la variable a, la función podrá usar este valor para sus
cálculos, pero está garantizado que los mismos no afectan en absoluto a la
variable en sí misma.
Como veremos más adelante, es posible que una función modifique a una
variable, pero para ello, será necesario comunicarle la DIRECCION EN MEMORIA
de dicha variable .
Las funciones pueden ó no devolver valores al programa invocante. Hay
funciones que tan sólo realizan acciones, como por ejemplo clrscr(), que
borra la pantalla de video, y por lo tanto no retornan ningun dato de
interés; en cambio otras efectuan cálculos, devolviendo los resultados de
los mismos.
La invocación a estos dos tipos de funciones difiere algo, por ejemplo:
clrscr() ;
a =
getch() ;
donde en
el segundo caso el valor retornado por la función se asigna a la variable a.
Obviamente ésta deberá tener el tipo correcto para alojarla .
Antes de
escribir una función es necesario informarle al Compilador los tamaños de
los valores que se le enviarán en el stack y el tamaño de los valores que
ella retornará al programa invocante .
Estas informaciones están contenidas en la DECLARACION del PROTOTIPO DE LA
FUNCION.
Formalmente dicha declaración queda dada por :
tipo del
valor de retorno nombre_de_la_función(lista de tipos de parámetros)
algunos
ejemplos :
float
la_funcion(int i, double j ) ;
double
otra_funcion(void) ;
otra_mas(long p) ;
void
ultima_funcion(long double z, char y, int x, unsigned long w) ;
El primer término del prototipo da, como hemos visto el tipo del dato
retornado por la función; en caso de obviarse el mismo se toma, por omisión,
el tipo int. Sin embargo, aunque la función devuelva este tipo de dato, para
evitar malas interpretaciones es conveniente explicitarlo .
Ya que el "default" del tipo de retorno es el int, debemos indicar cuando la
función NO retorna nada, esto se realiza por medio de la palabra VOID ( sin
valor).
De la misma manera se actúa, cuando no se le enviarán argumentos.
Más adelante se profundizará sobre el tema de los argumentos y sus
características.
La declaración debe anteceder en el programa a la definición de la función.
Es normal, por razones de legibilidad de la documentación, encontrar todas
las declaraciones de las funciones usadas en el programa, en el HEADER del
mismo, junto con los include de los archivos *.h que tienen los prototipos
de las funciones de Librería.
Si una ó más de nuestras funciones es usada habitualmente, podemos disponer
su prototipo en un archivo de texto, e incluirlo las veces que necesitemos,
según se vio en capítulos previos.
Definición de Funcion
La
definición de una función puede ubicarse en cualquier lugar del programa,
con sólo dos restricciones: debe hallarse luego de dar su prototipo, y no
puede estar dentro de la definición de otra función ( incluida main() ).
Ambito
de las Variables
Variables Globales
Hasta ahora hemos diferenciado a las variable segun su "tipo" (int, char
double, etc), el cual se refería, en última instancia, a la cantidad de
bytes que la conformaban. Veremos ahora que hay otra diferenciación de las
mismas, de acuerdo a la clase de memoria en la que residen.
Si definimos una variable AFUERA de cualquier función (incluyendo esto a
main() ), estaremos frente a lo denominado VARIABLE GLOBAL. Este tipo de
variable será ubicada en el segmento de datos de la memoria utilizada por el
programa, y existirá todo el tiempo que esté ejecutandose este .
Este tipo de variables son automaticamente inicializadas a CERO cuando el
programa comienza a ejecutarse .
Son accesibles a todas las funciones que esten declaradas en el mismo, por
lo que cualquiera de ellas podrá actuar sobre el valor de las mismas. Por
ejemplo :
#include
double
una_funcion(void);
double
variable_global ;
main()
{
double
i ;
printf("%f", variable_global ); /* se imprimirá 0 */
i =
una_funcion() ;
printf("%f", i ); /* se imprimirá 1 */
printf("%f", variable_global ); /* se imprimirá 1 */
variable_global += 1 ;
printf("%f", variable_global ); /* se imprimirá 2 */
return
0 ;
}
double
una_funcion(void)
{
return( variable_global += 1) ;
}
Observemos que la variable_global está definida afuera de las funciones del
programa, incluyendo al main(), por lo que le pertenece a TODAS ellas. En el
primer printf() del programa principal se la imprime, demostrandose que está
automaticamente inicializada a cero .
Luego es incrementada por una_funcion() que devuelve ademas una copia de su
valor, el cual es asignado a i ,la que, si es impresa mostrará un valor de
uno, pero tambien la variable_global ha quedado modificada, como lo
demuestra la ejecución de la sentencia siguiente. Luego main() tambien
modifica su valor , lo cual es demostrado por el printf() siguiente.
Esto nos permite deducir que dicha variable es de uso público, sin que haga
falta que ninguna función la declare, para actuar sobre ella.
Las globales son a los demás tipos de variables, lo que el GOTO es a los
otros tipos de sentencias .
Puede resultar muy difícil evaluar su estado en programas algo complejos,
con múltiples llamados condicionales a funciones que las afectan, dando
comunmente orígen a errores muy engorrosos de corregir .
Variables Locales
A
diferencia de las anteriores, las variables definidas DENTRO de una función,
son denominadas VARIABLES LOCALES a la misma, a veces se las denomina
también como AUTOMATICAS, ya que son creadas y destruídas automaticamente
por la llamada y el retorno de una función, respectivamente .
Estas variables se ubican en la pila dinámica (stack) de memoria
,destinandosele un espacio en la misma cuando se las define dentro de una
función, y borrándose cuando la misma devuelve el control del programa, a
quien la haya invocado.
Este método permite que, aunque se haya definido un gran número de variables
en un programa, estas no ocupen memoria simultaneamente en el tiempo, y solo
vayan incrementando el stack cuando se las necesita, para luego, una vez
usadas desaparecer, dejando al stack en su estado original .
El identificador ó nombre que se la haya dado a una variable es sólo
relevante entonces, para la función que la haya definido, pudiendo existir
entonces variables que tengan el mismo nombre, pero definidas en funciones
distintas, sin que haya peligro alguno de confusión .
La ubicación de estas variables locales, se crea en el momento de correr el
programa, por lo que no poseen una dirección prefijada, esto impide que el
compilador las pueda inicializar previamente. Recuerdese entonces que, si no
se las inicializa expresamente en el momento de su definición, su valor será
indeterminado (basura) .
Variables locales estáticas
Las variables locales vistas hasta ahora, nacen y mueren con cada llamada y
finalización de una función, sin embargo muchas veces sería util que
mantuvieran su valor, entre una y otra llamada a la función sin por ello
perder su ámbito de existencia, es decir seguir siendo locales sólo a la
función que las defina. En el siguiente ejemplo veremos que esto se consigue
definiendo a la variable con el prefacio static.
Variables de registro
Otra
posibilidad de almacenamiento de las variables locales es, que en vez de ser
mantenidas en posiciones de la memoria de la computadora, se las guarde en
registros internos del Microprocesador que conforma la CPU de la misma .
De esta manera el acceso a ellas es mucho más directo y rápido, aumentando
la velocidad de ejecución del programa. Se suelen usar registros para
almacenar a los contadores de los FOR, WHILE, etc.
Lamentablemente, en este caso no se puede imponer al compilador, este tipo
de variable, ya que no tenemos control sobre los registros libres en un
momento dado del programa, por lo tanto se SUGIERE, que de ser posible,
ubique la variable del modo descripto.
Variables Externas
Al
definir una variable, como lo hemos estado haciendo hasta ahora, indicamos
al compilador que reserve para la misma una determinada cantidad de memoria,
(sea en el segmento de memoria de datos, si es global ó en el stack, si es
local), pero debido a que en C es normal la compilación por separado de
pequeños módulos, que componen el programa completo, puede darse el caso que
una función escrita en un archivo dado, deba usar una variable global
definida en otro archivo. Bastará para poder hacerlo, que se la DECLARE
especificando que es EXTERNA a dicho módulo, lo que implica que está
definida en otro lado .
|