UC3M

Grado en Ing. Telemática/Sist. Audiovisuales/Sist. de Comunicaciones

Arquitectura de Sistemas

Septiembre 2017 - Enero 2018

Capítulo 3. Declaración de variables

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.

3.1. Ámbito 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.

3.1.1. Variables globales

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
1
2
3
4
5
int number = 10;
int main(int argc, char *argv[]) 
{
    return 1;
}
1
2
3
4
5
6
extern int number;
int function() 
{
    number++;
    return number;
}

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.

Sugerencia

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.

3.1.2. Variables estáticas

Cualquier declaración de una variable puede tener el prefijo static. Las variables estáticas en C tienen las siguientes dos propiedades:

  1. No pueden ser accedidas desde otro fichero. Por tanto, los prefijos extern y static no pueden ser utilizados en la misma declaración.

  2. 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:

  1. 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.

  2. 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.

Sugerencia

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.

3.1.3. Ensombrecimiento de variables

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.

Sugerencia

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.