Tabla de contenidos
En Java, el acceso a campos y métodos de un objeto puede ser controlado mediante tres ámbitos: público, privado y protegido. El lenguaje C dispone de reglas más simples para controlar lo que se conoce como el “ámbito de validez” de una variable.
El “ámbito de validez” de una variable está compuesto por las porciones de código desde donde se puede acceder y manipular su contenido. Estas porciones suelen corresponder con diferentes bloques de código escritos entre llaves (“{}”). Por ejemplo, cuando una función llama a otra, se abre un nuevo ámbito (el de la función llamada) dentro de otro (el de la función que llama) que desaparece cuando termina la función. Hay ciertas variables con ámbitos de validez intuitivos, como por ejemplo las que se definen al comienzo de una función. Pero C permite modificar estos ámbitos mediante el uso de prefijos en la declaración.
Toda variable declarada fuera de las funciones tiene ámbito global, es decir, puede ser accedida desde cualquier parte del programa. El siguiente código muestra un ejemplo de esta situación.
1 2 3 4 5 6 7 8 9 10 11 12 13 | int number; int function() { number++; return number; } int main(int argc, char *argv[]) { number = 0; function(); return 0; } |
La variable number
se declara en la línea 1,
fuera de una función, y por tanto es global. A continuación se accede en
las líneas 4 y 10.
Pero para acceder correctamente a una variable global hay
que cumplir dos requisitos más derivados de la forma en cómo el compilador
procesa los ficheros. Si la variable está declarada en el mismo fichero,
esta declaración debe preceder su uso (el compilador sólo lee el fichero
en un único paso). Si la variable está declarada en otro fichero, se debe
incluir en el fichero exactamente la misma declaración pero con el prefijo
“extern
” (el compilador sólo recuerda
información del fichero que está procesando).
Fichero 1 | Fichero 2 | ||||
---|---|---|---|---|---|
|
|
La variable number
se define como global en
la línea 1 del fichero 1. Para poder acceder a ella en la línea 1 del
fichero 2 se replica su declaración (sin inicializar) añadiendo el prefijo
“extern
”. La variable global se accede en la
línea 4 del fichero 2. Si se omite la línea 1 de este fichero, el
compilador emite el error de que la variable number
no ha
sido declarada. Si en el fichero 2 se incluye la definición sin el prefijo
“extern
”, el compilador notificará el error de
que se ha definido una variable múltiples veces.
Copia y pega el contenido de los dos ficheros del
ejemplo en los ficheros fichero1.c
y
fichero2.c
. Compila con el comando gcc
-Wall -o programa fichero1.c fichero2.c. Comprueba que se
genera el ejecutable con nombre programa
y
ejecútalo con el comando ./programa. No debe imprimir
nada por pantalla. Haz cambios en las declaraciones para comprobar las
reglas que impone el compilador.
Cualquier declaración de una variable puede tener el
prefijo “static
”. Las variables estáticas en C
tienen las siguientes dos propiedades:
No pueden ser accedidas desde otro fichero. Por tanto,
los prefijos “extern
” y
“static
” no pueden ser utilizados en la
misma declaración.
Mantienen su valor a lo largo de toda la ejecución del programa independientemente del ámbito en el que estén definidas.
Como consecuencia de estas dos propiedades se derivan los siguientes casos:
Si una variable estática está declarada fuera de las funciones, será accessible únicamente por el código que le siga en el mismo fichero de su declaración.
Si una variable estática está declarada en una función, sólo será accesible desde esa función y mantendrá su valor entre ejecuciones de la función.
Este comportamiento es contra intuitivo porque estas variables se declaran en el mismo lugar que el resto de variables en una función, pero mientras que estas adquieren valores nuevos con cada ejecución, las estáticas conservan estos valores entre ejecuciones.
El siguiente programa muestra un ejemplo del comportamiento de una variable estática definida en una función.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h> int funcion() { static int número = 10; /* Variable estática */ número++; /* Mantiene el valor de la ejecución anterior */ return número; } int main() { /* Imprime el resultado de dos invocaciones a la función */ printf("%d\n", funcion()); /* Imprime el número 11 */ printf("%d\n", funcion()); /* ¡Imprime el número 12! */ return 0; } |
La línea 4 declara la variable estática
número
y la inicializa con el valor 10. La primera vez que
se ejecuta esta función, por tanto, la variable tiene este valor al
comienzo y se incrementa. Por este motivo, la línea 11 imprime el valor 11
por pantalla. Pero al ser invocada por segunda vez la función, la variable
número
mantiene su valor y por tanto se incrementa a 12, que
es lo que se imprime en la línea 12.
Copia y pega el programa del ejemplo anterior en un fichero de texto en tu entorno de desarrollo. Compila con el comando gcc -o programa fichero.c reemplazando fichero.c por el nombre del fichero que hayas creado. Ejecuta el programa con el comando ./programa y comprueba que el resultado coincide con la explicación.
El siguiente ejemplo muestra el comportamiento de una variable estática definida fuera de una función.
Fichero 1 | Fichero 2 |
---|---|
/* Variable global */
int numero = 10;
int main(int argc, char *argv[])
{
numero++;
return 0;
}
/* Accesible en este fichero a partir de este punto */
static int coeficiente = 20;
void funcion2()
{
numero++;
coeficiente++;
}
|
extern int numero;
/* Variable coeficiente no
es accesible desde este fichero */
int funcion()
{
numero++;
return numero;
}
|
La variable numero
se declara global y puede
ser accedida por otro fichero siempre y cuando se incluya su declaración
con el prefijo “extern
”. Sin embargo, la
variable coeficiente
sólo es accesible en el fichero en el
que se declara y a partir de esa declaración (no está visible en la
función main
.
El problema de “ensombrecimiento” se produce cuando en un ámbito de validez se define una variable con nombre idéntico a otra válida en un ámbito superior. El siguiente ejemplo ilustra esta situación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h> int numero = 10; void funcion() { int numero; /* Colisiona con variable global */ numero = 20; /* ¿A qué variable se le asigna el valor? */ } int main(int argc, char *argv[]) { funcion(); /* Imprime el valor de numero */ printf("%d\n", numero); /* ¿Qué valor imprime? */ return 0; } |
Las declaraciones de las líneas 2 y 5 son idénticas pero
están hechas en ámbitos diferentes (global frente a local a la
función). El compilador permite esta declaración. Pero cuando se ejecuta
la función se produce el ensombrecimiento: la variable global
numero
no puede ser accedida, pues ese nombre se refiere a la
variable local a la función. Al terminar la función, la variable global ya
no está “ensombrecida” y vuelve a ser accesible y se imprime
su valor (10) en la línea 12.
Copia y pega este programa en un fichero de texto en tu entorno de desarrollo. Añade más declaraciones, compila y ejecuta el programa para verificar la política de ensombrecimiento del compilador.