CONTENIDO DE LA LECCIÓN 14
VARIABLES LOCALES Y ALCANCE
1. Introducción 2
2. Declaración de variables locales 4
2.1. Ejemplo 14.1 4
3. Conflicto entre nombres de las variables 4
3.1. Ejemplo 14.2 5
4. Variables globales 5
4.1. Ejemplo 14.3 7
5. Alcance de una variable 8
6. Ámbito de las constantes 9
7. Reglas de alcance 9
7.1. Ejemplo 14.4 10
8. Operador unario de resolución de alcance 12
8.1. Ejemplo 14.5 12
9. Variables estáticas 13
9.1. Ejemplo 14.6 13
10. Examen 31 17
11. Lo que necesita saber 14
12. Preguntas y problemas 15
12.1. Preguntas 15
LECCIÓN 14
VARIABLES LOCALES Y ALCANCE
INTRODUCCIÓN
Hemos utilizado identificadores para los nombres de las variables. Los atributos de las variables incluyen el nombre, clase, tamaño y valor. También utilizamos identificadores como nombres de funciones definidas por el usuario. De hecho, los identificadores de los programas tienen otros atributos, incluyendo clase de almacenamiento, alcance y enlace.
C++ ofrece cuatro especificadores de clase de almacenamiento: auto, register, extern y static. El especificador de clase de almacenamiento de un identificador permite determinar su clase de almacenamiento, alcance y enlace.
La clase de almacenamiento del identificador determina el periodo durante el cual existe en memoria dicho identificador. Algunos identificadores existen durante un periodo breve, otros se crean y destruyen repetidamente y otros existen durante toda la ejecución del programa.
El alcance de un identificador es dónde puede ser referenciado en el programa. Algunos pueden referenciarse a lo largo de todo el programa, mientras que otros sólo en ciertas partes. El alcance de un identificador determina, en los programas con múltiples archivos fuente, si es conocido sólo en el archivo fuente actual o en cualquier archivo fuente que tenga las declaraciones apropiadas.
En esta lección estudiaremos los cuatro especificadores de clase de almacenamiento y las dos clases de almacenamiento.
Los especificadores de clase de almacenamiento pueden dividirse en clase de almacenamiento automático y clase de almacenamiento estático. Las palabras clave auto y register se utilizan para declarar variables de clase de almacenamiento automático. Tales variables se crean al entrar en el bloque en el que se declaran, existen mientras el bloque está activo y se destruyen al salir de él.
Las variables que declara dentro de una función se llaman variables locales. Sus valores son conocidos exclusivamente por la función. En otras palabras si declara una variable local llamada salario dentro de la función nomina, las otras funciones no tienen acceso al valor de salario.
Sólo las variables pueden ser de la clase de almacenamiento automático. Las variables locales y los parámetros de una función normalmente son de la clase de almacenamiento automático. El especificador auto declara explícitamente variables de clase de almacenamiento automático. Por ejemplo, la siguiente declaración indica que las variables float x y y son variables de la clase de almacenamiento automático, es decir, existen sólo en el cuerpo de la función en la que aparece su definición:
auto float x, y;
De manera predeterminada, las variables locales son de la clase de almacenamiento automático, por lo que pocas veces se utiliza la palabra clave auto.
Los datos en la versión de lenguaje de máquina de un programa normalmente se cargan en registros para efectuar cálculos y otros procesamientos. El compilador puede ignorar las declaraciones register. Por ejemplo, podría ser que el compilador no tuviera suficientes registros disponibles. La siguiente declaración sugiere que se coloque a la variable entera contador en uno de los registros de la computadora; sin importar que el compilador lo hago o no, contador se inicializa a 1:
register int contador = 1;
Con las palabras clave extern y static se declaran identificadores para las variables y funciones de la clase de almacenamiento estático. Tales variables existen desde el momento en que inicia la ejecución del programa. Para las variables, el almacenamiento está asignado y se inicializa con la ejecución del programa. Para las funciones, su nombre existe cuando inicia la ejecución del programa. Sin embargo, aunque las variables y los nombres de función existan desde el inicio de la ejecución, esto no significa que puedan utilizarse en todo el programa. La clase y alcance (dónde puede usarse un nombre) del almacenamiento son temas independientes que veremos en otra parte.
Hay dos tipos de identificadores con la clase de almacenamiento estático: identificadores externos (como las variables y los nombres de funciones globales) y variables locales declaradas con el especificador de clase de almacenamiento static. Lo predeterminado para las variables y nombres de funciones globales es el especificador de clase de almacenamiento extern. Las variables globales se crean escribiendo declaraciones de variables fuera de todas las definiciones de funciones. Dichas variables conservan sus valores a lo largo de la ejecución del programa. Las variables y funciones globales pueden ser referenciadas por cualquier función que siga a su declaración o definición en el archivo.
Las variables locales declaradas con la palabra clave static siguen siendo conocidas sólo en la función en la que se definen pero, a diferencia de las variables automáticas, las variables static locales conservan sus valores al salir de la función. La siguiente vez que se llama a la función, las variables locales static contendrán los valores que tenían al salir de ella la última vez. La siguiente instrucción declara la variable local contador como de tipo static y la inicializa a 1.
static int contador = 1;
Todas las variables numéricas de la clase de almacenamiento estático se inicializan a cero si el programador no las inicializa a un valor explícito. (Las variables estáticas de apuntadores también se inicializan a cero)
Los especificadores de clase de almacenamiento extern y static tienen significados especiales cuando se aplican a identificadores externos.
Objetivos de esta lección:
•••• Declarar variables locales, globales y estáticas dentro de una función.
•••• Comprender lo que es el alcance de una variable (los lugares dentro de su programa en los cuales
las variables son conocidas y accesibles)
•••• Crear y utilizar variables globales.
•••• Utilizar el operador unitario de resolución de alcance (::).
DECLARACIÓN DE VARIABLES LOCALES
Una variable local es aquella definida dentro de un bloque de códigos específico, como una función. La variable se dice que es local, ya que solo es conocida por la función que la define. Las variables locales se declaran al principio de la función, después de la llave de inicio de agrupación. No confunda variables locales con los parámetros de la función listados en el encabezado de la función. Las variables locales de una función se definen después de abrir la llave de una función y los parámetros se definen en el encabezado de la función.
Ejemplo 14.1
El siguiente programa: USABEEP.CPP, usa la función sonarBocina, la cual suena la bocina de la computadora el número de veces especificado por el parámetro sonidos. Con la función sonarBocina, la variable local contador, controla el número de veces que la bocina a sonado.
CONFLICTO ENTRE NOMBRES DE LAS VARIABLES
Cuando declara variables locales dentro de una función, es muy probable que utilice el mismo nombre en dos o más funciones distintas. Ya que las variables solo son conocidas por la función que la define, en realidad no existe ningún conflicto.
/* El siguiente programa: USABEEP.CPP, usa la función sonarBocina, la cual suena la bocina de la computadora el número de veces especificado por el parámetro sonidos.
*/
#include <iostream.h> //Para cout y cin
void sonarBocina(int sonidos) {
int contador; //contador es una variable local
for (contador = 1; contador <=sonidos; contador++) cout << '\a';
}//Fin de sonarBocina()
void main(void) {
sonarBocina(2);
sonarBocina(3);
}//Fin de main()
Ejemplo 14.2
El siguiente programa LCLNOM.CPP, utiliza la función sumarValores para sumar dos valores enteros. La función asigna el resultado a la variable local valor. En la función main(), sin embargo, uno de los parámetros pasados a la función se llama valor. Veremos que en efecto no hay conflicto:
VARIABLES GLOBALES
Para declarar variables globales en C++, deberá definirlas fuera de la función main() Esto las hace globales a la función main(), así como a cualquiera de las funciones llamadas por main() Desde luego, esto deberá ser para todas las funciones definidas en un programa dado.
Esta es la razón del porque en C++ a las variables globales algunas veces se les llamada variables de archivo.
Para mostrar el uso de las variables locales y globales, veamos la estructura de bloque en la figura 14.1. En ésta, la variable global es X0 y la variable local es X1. La función puede realizar operaciones sobre ambas X0 y X1. Sin embargo, main() puede operar sólo con X0, porque X1 no está definida fuera del bloque funcion1() Cualquier intento de usar X1 fuera de la funcion1() derivará en un error. Además, cualquier valor asignado a X1 durante la ejecución de la funcion1() se destruye y no se puede recuperar para posteriores ejecuciones de la función.
/* El siguiente programa: LCLNOM.CPP, ilustra el uso de dos variables locales: valor con el mismo nombre en dos funciones distintas.
*/
#include <iostream.h> //Para cout y cin
int sumarValores(int a, int b) {
int valor; //valor es una variable local definida en sumarValores()
valor = a + b;
return (valor);
}//Fin de sumarValores()
void main(void) {
int valor = 1001; //valor es una variable local definida en main() int otroValor = 2002;
cout << valor << " + " << otroValor << " = " <<
sumarValores(valor, otroValor) << endl;
}//Fin de main()
Figura 14.1. X0 es global para todo el programa y X1 es local para funcion1()
Ahora vea la figura 14.2. Esta vez, hay dos funciones, ambas definen X1 como una variable local. Otra vez, la variable global es X0 y puede usarse dentro de main(), así como dentro de cualquier otra función definida en el programa. La variable X1 se puede usar sólo dentro de las dos funciones. Sin embargo, cualquier operación sobre X1 dentro de la funcion1() no afectará el valor de X1 en funcion2() y viceversa. En otras palabras, X1 en funcion1() se considera una variable independiente de X1 en funcion2() ¡Es como si éstas fueran dos variables completamente diferentes! Ello proporciona una característica muy importante de la programación estructurada, llamada modularidad.
Figura 14.2. X0 es global para todo el programa, y X1 es local para funcion1() y funcion2(), pero se tratan como dos variables únicas.
Para comprender la importancia de la modularidad, suponga que es un miembro de un equipo de programación que deberá desarrollar el software para solucionar un problema
int X0; // Variable global funcion1() prototipo;
void main(void) {
...
...
} // Fin de main()
funcion1() {
int X1; // Variable local ...
...
} // Fin de funcion1()
int X0; // Variable global funcion1() prototipo;
funcion2() prototipo;
void main(void) {
...
} // Fin de main()
funcion1() {
int X1; // Variable local ...
...
} // Fin de funcion1()
funcion2() {
int X1; // Variable local ...
...
} // Fin de funcion2()
industrial muy complejo. La manera más fácil de solucionar cualquier problema complejo es dividirlo en subproblemas más simples y manejables. Después, solucionar los subproblemas y combinar sus respuestas para resolver el problema complejo general. Esto se llama diseño descendente. Usando el enfoque de diseño descendente, el líder del equipo divide el problema de programación complejo en subproblemas más simples y después pide a cada miembro del equipo escribir una función para solucionar un subproblema dado. ¿Cómo se relaciona esto con el uso de variables locales? Bien, es posible escribir una función utilizando cualquier variable local deseada, sin preocuparse de que otro miembro del equipo use la misma variable local. Aún si dos miembros del equipo usan los mismos identificadores de variable local, las funciones se ejecutarán independientemente cuando se combinen en el programa principal. Esto permite al equipo un enfoque descendente para diseño de software, algo que no es posible en lenguajes no estructurados como BASIC. Las funciones en un lenguaje estructurado actúan como bloques de construcción modular para realizar el programa general.
Ejemplo 14.3
El siguiente programa, GLOBAL.CPP, utiliza una variable global llamada numero. Cada una de las funciones del programa pueden utilizarla (o cambiarla) En este caso, cada función visualiza el valor actual de la variable, posteriormente incrementa su valor en 1.
Como regla general, su programa debe evitar el uso de variables globales. Ya que cada función puede cambiar el valor de la variable global, es bastante difícil tener un control de que función es la que la altera.
/* El siguiente programa: GLOBAL.CPP, ilustra el uso de las variables globales */
#include <iostream.h> //Para cout y cin int numero = 1001; //Variable global
void primerCambio(void) {
cout << "El valor de número en primerCambio es: " <<
numero << endl;
numero++;
}//Fin de primerCambio()
void segundoCambio(void) {
cout << "El valor de número en segundoCambio es: " <<
numero << endl;
numero++;
}//Fin de segundoCambio()
void main(void) {
cout << "El valor de número en el programa principal es de: "
<< numero << endl;
numero++;
primerCambio();
segundoCambio();
}//Fin de main()
ALCANCE DE UNA VARIABLE
El término alcance o ámbito define la ubicación en el programa donde el nombre de una variable tiene significado (y por lo tanto puede ser utilizada) Para una variable local, su alcance está restringido a la función en la que la variable es declarada. Las variables globales, por otra parte, son conocidas a través de todo el programa. Como resultado la variable global tiene un alcance mucho más amplio.
Una variable global definida antes que main() tiene un alcance (ámbito) de archivo, porque es accesible a cualquier bloque en el mismo archivo. Por otra parte, se dice que una variable local definida en una función tiene alcance (ámbito) de bloque, porque está disponible solamente dentro del bloque de la función en la cual se define.
El alcance o ámbito de una variable se refiere al bloque más grande en el que se tiene acceso a una variable dada.
Un término a menudo asociado con ámbito es visibilidad. Se podría decir que la visibilidad de una variable global es el archivo de programa completo y la de una variable local es el bloque en el cual está definida. Vea el siguiente código para captar la idea.
/* El siguiente programa: ALCANCE1.CPP, muestra el concepto de alcance o ámbito de una variable.
*/
// Funciones prototipo void funcion1(void);
void funcion2(void);
int X0; // Global para el programa completo
void main(void) {
int X1; // Local para main() } // Fin de main()
int X2; // Global para funcion1() y funcion2()
void funcion1(void) {
int X3; // Local para funcion1() } // Fin de funcion1()
void funcion2(void) {
int X0; // Local para funcion2() } // Fin de funcion2()
En este caso, X0 tiene un ámbito de archivo, porque es visible o global para todos los bloques en el programa. Sin embargo, X1 tiene un ámbito de bloque, porque está definido como local para main() y además es visible solamente en main() y no se puede acceder por funcion1() o funcion2() Después, X2 está definido después de main(), pero antes de cualquier función. De esta manera, el ámbito de X2 se extiende a cualquier función definida después de X2, haciéndola visible para funcion1() y funcion2(), pero no para main() ¿Qué hay de X3? En este caso X3 se define dentro de funcion1() y por lo tanto, es local a esta función y no puede ser usada por
ninguna otra función, incluyendo main() Por último, observe que la variable X0 también se define en funcion2() como una variable local. ¿Hay un problema aquí, porque X0 también es global al programa completo? ¡No! La variable local X0 en funcion2() es independiente de la variable global X0 definida antes que main() Dado que X0 se define localmente en funcion2(), cualquier operación de X0 dentro de esta función no afecta la variable global X0. Es como si fueran dos variables separadas.
En resumen, podríamos hacer los siguientes enunciados en relación con el ámbito de las variables en el programa anterior.
•••• X0 es visible para todo el programa. Sin embargo, X0 en funcion2() es diferente de X0 global.
•••• X1 es visible para main(), pero no para funcion1() o funcion2()
•••• X2 tiene un ámbito que se extiende a funcion1() y funcion2(), pero no a main()
•••• X3 tiene un ámbito de bloque de la funcion1()
Todas las funciones excepto funcion2() tienen acceso a la variable global X0. Como resultado, el valor de X0, se puede modificar con cualquiera de estas funciones. La modificación de una variable global por medio de una función se conoce como un efecto lateral. En la mayoría de los casos, los efectos laterales no son deseables. No es una buena práctica modificar las variables globales dentro de una función, porque destruye la característica de modularidad de un lenguaje estructurado. Por lo tanto, siempre defina sus objetos variables de una manera tan local como sea posible dentro de un bloque de función dado. Una excepción a esta regla es cuando algunas funciones necesitan compartir una variable o estructura de datos comunes.
ÁMBITO DE LAS CONSTANTES
Las constantes al igual que las variables, también pueden denominarse locales o globales. Dicho de otro modo, es posible declarar constantes en forma global antes de main() o en forma local dentro de una función. El ámbito de una constante funciona como el de una variable. Su ámbito es el bloque más grande en el cual está disponible. Sin embargo, la regla general para el enunciado de constantes es lo opuesto de la definición de variables – deberá declarar objetos constantes de una manera tan global como sea posible. En otras palabras, todas las constantes deberán declararse antes de main() si es posible.
El enunciado global de constantes permite a todas las funciones tener acceso a una constante dada. Ningún efecto lateral es posible, porque el programa no puede cambiar una constante. Más aún, las constantes no siempre son constantes. Cuando se deban cambiar, solamente necesita hacer una modificación en un lugar en el programa si éstas se declaran globalmente. Sin embargo, si se declaran de manera local, debe hacerse un cambio en cada función en la cual están declaradas.
REGLAS DE ALCANCE
La parte del programa en la que un identificador tiene significado se conoce como alcance. Por ejemplo, cuando declaramos una variable local en un bloque, podemos referenciarla sólo en dicho bloque o en los bloques anidados dentro de él. Los cuatro alcances de un identificador son alcance de función, alcance de archivo, alcance de bloque y alcance de prototipo de función. Existe un quinto tipo de alcance que no veremos en este curso y que se llama alcance de clase.
Los identificadores declarados fuera de cualquier función tienen un alcance de archivo.
Tales identificadores son conocidos por todas las funciones desde el momento en que se declara el identificador hasta el final del archivo. Las variables globales, las definiciones de funciones y los prototipos de función colocadas fuera de una función tienen alcance de archivo.
Las etiquetas (identificadores seguidos por dos puntos, como inicio) son los únicos identificadores con alcance de función. Las etiquetas pueden utilizarse en cualquier parte de la función en la que aparezcan, pero no pueden referenciarse desde fuera del cuerpo de la función.
Las etiquetas sirven en las estructuras switch (como etiquetas case) y en las instrucciones goto.
Las etiquetas son detalles de implementación que las funciones esconden de las demás funciones.
Este ocultamiento –llamado más formalmente ocultamiento de información- es uno de los principios más fundamentales de la buena ingeniería de software.
Los identificadores declarados dentro de un bloque tienen alcance de bloque. Este comienza en la declaración del identificador y termina con la llave derecha de terminación del bloque (}) Las variables locales declaradas al inicio de una función tienen un alcance de bloque, así como los parámetros de función, que también son variables locales de la función. Cualquier bloque puede contener declaraciones de variables. Cuando los bloques son anidados y un identificador de un bloque externo tiene el mismo nombre que un identificador de un bloque interno, se oculta el identificador del bloque externo hasta que termina el bloque interno.
Durante la ejecución del bloque interno, éste ve el valor de su propio identificador local, y no el del identificador con nombre idéntico del bloque superior que lo rodea. Las variables locales declaradas como static continúan teniendo alcance de bloque, aun cuando existen desde el comienzo de la ejecución del programa. La duración del almacenamiento no afecta el alcance de los identificadores.
Los únicos identificadores con alcance de prototipo de función son aquellos que se utilizan en la lista de parámetros de un prototipo de función. Como se mencionó antes, los prototipos de función no requieren nombres en la lista de parámetros; solo se necesitan los tipos.
Si se indica un nombre en la lista de parámetros de un prototipo de función, el compilador lo ignora. Los identificadores utilizados en los prototipos de función pueden reutilizarse en cualquier otra parte del programa sin ninguna ambigüedad.
Ejemplo 14.4
El siguiente programa ALCANCE2.CPP, ilustra los conceptos relacionados con el alcance de las variables globales, variables automáticas locales y variables static locales
.
/* El siguiente programa: ALCANCE2.CPP, ilustra los conceptos relacionados con el alcance de las variables globales, vaariables automáticas locales y vaariables static locales.
*/
#include <iostream.h> //Para cout y cin
void a(void); //Prototipo de función void b(void); //Prototipo de función void c(void); //Prototipo de función
int x = 1; //Variable global
void main(void) {
int x = 5; //Variable local del main()
cout << "La variable local x en el alcance externo de main() es "
<< x << endl;
{
int x = 7; //Inicia nuevo alcance
cout << "La variable local x en el alcance interno de main() es "
<< x << endl;
//Termina nuevo alcance }//Fin del bloque
cout << "La variable local x en el alcance externo de main() es "
<< x << endl;
a(); //La función a() tiene una variable x automática local
b(); //La función b() tiene una x estática local c(); //La función c() utiliza la x global;
a(); //La función a() reinicializa la x automática local b(); //La x estática local conserva su valor previo c(); //La x global también conserva su valor previo cout << "La variable local x en main() es: " << x << endl;
}//Fin de main()
void a(void) {
int x = 25; //Se inicializa cada vez que se llama a a
cout << endl << "La variable local x en la función a() es "
<< x << " después de entrar a a()" << endl;
++x;
cout << "La variable local x en la función a() es " << x << " antes de salir de a()" << endl;
}//Fin de la función a()
void b(void) {
static x = 50; //Sólo inicialización estática la primera vez que se llama a b
cout << endl << "La variable local estática x es " << x << " al entrar a la función b()" << endl;
++x;
cout << "La variable local estática x es " << x << " al salir de b()" << endl;
}//Fin de la función b()
void c(void) {
cout << endl << "La variable global x es " << x <<
" al entrar a la función c()" << endl;
x *= 10;
cout << "La variable global x es " << x << " al salir de c()" << endl;
}//Fin de la función c()
Se declara e inicializa a 1 una variable global x. Dicha variable está oculta en cualquier bloque (o función) en la que se declare una variable llamada también x. En main() se declara e inicializa a 5 una variable local x. Esta variable se imprime para mostrar que la x global está oculta en main() A continuación se define un nuevo bloque en main() con otra variable local x inicializada a 7. Esta variable se imprime para mostrar que oculta a la x en el bloque externo de main() La variable x con el valor 7 se destruye de manera automática al salir del bloque, y la variable local x del bloque externo de main() se imprime, mostrando que no está oculta. El programa define tres funciones; ninguna toma argumentos ni devuelve nada. La función a() define la variable automática x y la inicializa a 25. Al llamar a a(), se imprime, incrementa y vuelve a imprimir la variable antes de salir de la función. Cada vez que se llama a esta función, se vuelve a crear la variable x y se inicializa a 25. La función b() declara la variable static x y la inicializa a 50. Las variables locales declaradas como static conservan sus valores aun cuando están fuera del alcance. Al llamar a b(), se imprime, incrementa y vuelve a imprimir x antes de salir de la función. En la siguiente llamada a esta función, la variable local static x tendrá el valor 51. La función c() no declara ninguna variable. Por ello, cuando hace referencia a la variable x, se toma la variable global x. Al llamar a c(), se imprime la variable global, se multiplica por 10 y se vuelve a imprimir antes de salir de la función. La siguiente vez que se llama a c(), la variable global tiene su valor modificado, 10. Por último, el programa imprime nuevamente en main() la variable local x, para mostrar que ninguna de las llamadas a funciones ha modificado el valor de x, ya que todas ellas han hecho referencia a variables de otros alcances.
OPERADOR UNARIO DE RESOLUCIÓN DE ALCANCE
Es posible declarar variables locales y globales con el mismo nombre. C++ tiene el operador unario de resolución de alcance (::), que accede a una variable global cuando dentro del alcance hay una variable local con el mismo nombre. Dicho operador no sirve para acceder una variable local de un bloque exterior que tenga el mismo nombre. Es posible acceder directamente a una variable global sin el operador unario de resolución de alcance, si el nombre de la variable global no es igual al de una variable local dentro del alcance. (Nota: también existe el operador binario de resolución de alcance con clases)
Ejemplo 14.5
El siguiente programa: OPEUNA.CPP, presenta el operador unario de resolución de alcance con variables locales y globales que tienen el mismo nombre. Para subrayar que la versión local y la global de la variable constante PI son diferentes, el programa declara como double a una de las variables y como float a la otra.
/* El siguiente programa: OPEUNA.CPP, muestra el uso del operador unario de resolución de alcance.
*/
#include <iostream.h> //Para cout y cin
#include <iomanip.h> //Para setprecision const double PI = 3.14159265358979;
void main(void) {
const float PI = (float)(::PI);
VARIABLES ESTÁTICAS
Se estableció que cualquier valor asignado a una variable local dentro de una función se destruye y no puede ser recuperado por ejecuciones posteriores de la función. Esto es verdadero a menos que use la palabra reservada static frente a la definición de la variable. Haciendo una variable local estática, su valor se conserva de una llamada a la siguiente de la función en la cual está definida. Una variable estática actúa como una variable local puesto que no es accesible fuera del bloque de la función en la que se definió. Sin embargo, una variable estática tiene la duración de una variable global puesto que permanece definida a lo largo de la ejecución del programa.
Ejemplo 14.6
El siguiente programa: ESTATICA.CPP, es un programa que muestra esta idea:
De la función prototipo, se puede ver que ejemploEstatico() espera recibir un entero y no regresa ningún valor al programa llamador. Viendo la instrumentación de la función, es posible ver que la variable llamada contador se define como una variable local static dentro de la función. El valor de esta variable se muestra en la pantalla cada vez que se llama a la función.
cout << setprecision(20)
<< "El valor local de la constante PI, tipo float = " << PI << endl << "El valor global de la constante PI, tipo doble = "
<< ::PI << endl;
}//Fin de main()
/* El siguiente programa: ESTATICA.CPP, muestra el concepto de variable estática. */
#include <iostream.h> //Para cout y cin
// Funciones prototipo void ejemploEstatico(int);
void main(void)
{
ejemploEstatico(1);
ejemploEstatico(2);
ejemploEstatico(3);
} // Fin de main()
// Esta función muestra el uso de una variable estática void ejemploEstatico(int llamada)
{
static int contador = 0;
if(llamada == 1)
contador = 1;
cout << "El valor de contador en la llamada # " << llamada << " es: "
<< contador << endl;
++contador;
} // Fin de ejemploEstatico()
Después de que se muestra contador, su valor se incremente en el último enunciado de la función. En la primera llamada, contador se inicializará al valor 1 por medio del enunciado if dentro de la función. En todas las llamadas posteriores, el valor de contador no se inicializa. De esta manera, su valor tiene que ser el valor que tuvo cuando salió de la llamada de la función anterior. Observe que en la función main() se llama tres veces a ejemploEstatico() Esto es lo que verá en la pantalla:
El valor de contador en la llamada # 1 es: 1 El valor de contador en la llamada # 2 es: 2 El valor de contador en la llamada # 3 es: 3
Para que ocurra esta salida, el valor de la variable estática contador se retiene desde una llamada de una función a la siguiente. ¿Qué sucede si se elimina la palabra static frente a la definición del objeto variable? Aquí están los resultados obtenidos en la pantalla:
El valor de contador en la llamada # 1 es: 1 El valor de contador en la llamada # 2 es: 0 El valor de contador en la llamada # 3 es: 0
Como puede ver, contador se inicializa al valor 1 en la primera llamada. Sin embargo, su valor no se conservó en las llamadas posteriores. De hecho, no se puede predecir su valor en llamadas posteriores, porque podría ser cualquier valor arbitrario de memoria. Así, lo que hay que aprender aquí es que si usted quiere que una variable local mantenga su valor de una llamada a la función a la siguiente, defina la variable como una variable estática usando la palabra reservada static frente a la definición de variable local.
LO QUE NECESITA SABER
Antes de continuar con la siguiente lección, asegúrese de haber comprendido los siguientes conceptos:
!
!
!
!"""Las variables locales son aquellas declaradas dentro de una función. "
!
!
!
!"""Las variables locales son conocidas exclusivamente dentro de la función en las que fueron declaradas. "
!
!
!
!"""Dos o más funciones pueden utilizar el mismo nombre para declarar una variable local sin entrar en "
conflicto.
!!
!!"""Una variable global es aquella cuyo nombre y valor es conocido en todo el programa. "
!
!
!
!"""Para crear una variable global, declare la variable al principio de su programa fuente, fuera de cualquier "
función.
!!
!!"""Ya que las variables locales pueden ser cambiadas por cualquier función, introducen la posibilidad de "
errores dentro de su programa, los cuales pueden ser difíciles de detectar; Razón por la cual, sus programas deben evitar el uso de variables globales.
!
!
!
!"""El alcance de un identificador es la parte del programa en la que dicho identificador puede utilizarse. "
!
!
!
!"""Cada identificador de variable tiene los atributos de clase de almacenamiento, alcance y enlace. "
!
!
!
!"""C++ ofrece cuatro especificadores de almacenamiento: auto, register, extern y static. "
!
!
!
!"""La clase de almacenamiento de un identificador determina cuándo existe en la memoria. "
!
!
!
!"""El alcance de un identificador especifica dónde puede ser referenciado en el programa. "
EXAMEN BREVE 31
!
!
!
!"""El alcance de un identificador determina, en los programas con múltiples archivos fuente, si es conocido "
sólo en el archivo fuente actual o en cualquier archivo fuente que tenga las declaraciones adecuadas.
!
!
!
!"""Las variables de clase de almacenamiento automático se crean cuando se entra en el bloque donde están "
declaradas, existen mientras está activo el bloque y se destruyen al salir de él. Las variables locales de una función normalmente son de la clase de almacenamiento automático.
!
!
!
!"""El " especificador de clase register puede ponerse antes de la declaración de una variable automática para sugerirle al compilador que la mantenga en uno de los registros del hardware de la computadora, que son de alta velocidad. El compilador puede ignorar las declaraciones register. La palabra clave register sólo puede utilizarse con variables de la clase de almacenamiento automático.
!
!
!
!"""Las " palabras clave extern y static sirven para declarar identificadores para variables y funciones de la clase de almacenamiento estático.
!
!
!
!"""Las variables de la clase de almacenamiento estático se asignan e inicializan al inicio de la ejecución del "
programa.
!!
!!"""Dos tipos de identificadores tienen la clase de almacenamiento estático: los identificadores externos y "
las variables locales declaradas con el especificador de clase static.
!!
!!"""Las " variables globales se crean declarándolas fuera de cualquier definición de función y conservan sus valores a lo largo de la ejecución del programa.
!
!
!
!"""Las " variables locales declaradas como static conservan su valor al salir de la función donde están declaradas.
!
!
!
!"""Todas las variables numéricas de la clase de almacenamiento estático se inicializan a cero si el "
programador no lo hace explícitamente.
!
!
!
!"""Los cuatro alcances de un identificador son alcance de función, alcance de archivo, alcance de bloque y "
alcance de prototipo de función.
!!
!!"""Las etiquetas son los únicos identificadores con alcance de función. Se pueden utilizar en cualquier "
parte de la función en las que se encuentran, pero no pueden ser referenciadas fuera del cuerpo de dicha función.
!
!
!
!"""Un identificador que se declara fuera de cualquier función tiene un alcance de archivo. Este "
identificador es conocido desde el punto en que es declarado hasta el final del archivo.
!
!
!
!"""Los identificadores declarados dentro de un bloque tienen un alcance de bloque, el cual termina con la "
llave derecha (}) de terminación del bloque.
!
!
!
!"""Las variables locales declaradas al inicio de una función tienen un alcance de bloque, así como los "
parámetros de función, que son considerados por la función como variables locales.
!!
!!"""Cualquier bloque puede contener declaraciones de variables. Cuando se anidan los bloques y un "
identificador de un bloque exterior tiene el mismo nombre que otro de un bloque interior, se oculta el identificador del bloque exterior hasta que termina el bloque interior.
!
!
!
!"""Los únicos identificadores con alcance de prototipo de función son los indicados en la lista de "
parámetros de un prototipo de función. Los identificadores indicados en un prototipo de función pueden reutilizarse sin ambigüedades en cualquier otro lugar del programa.
PREGUNTAS Y PROBLEMAS PREGUNTAS
1. Explique la diferencia entre una variable local y una global.
2. ¿Qué significa el ámbito o alcance de una variable?
3. ¿Qué es el efecto lateral?
4. Verdadero o falso: Los objetos variables deberán definirse tan localmente como sea posible y los objetos constantes tan globalmente como sea posible.
5. Llene los siguientes espacios en blanco:
a. Los especificadores de clase de almacenamiento son _______________, _____________, _________ y ____________.
b. Las variables declaradas en un bloque o en la lista de parámetros de una función se toman como de la clase de almacenamiento ___________ a menos que se especifique algo diferente.
c. El especificador de clase de almacenamiento____ _______ es una recomendación al compilador para que almacene una variable en alguno de los registros de la computadora..
d. Las variables declaradas fuera de cualquier bloque o función son variables ___________________.
e. Para que las variables locales de una función conserven su valor entre las llamadas a función, se deben declarar con el especificador de clase de almacenamiento ________________.
f. Los cuatro alcances posibles de los identificadores son _______________, ________________, ___________ y __________.
6. Una variable local tiene un ámbito _________________.
7. Suponga que el objeto entero local llamado subTotal deberá mantener su valor desde una llamada de una función a la siguiente. Escriba una definición para este objeto.
8. Suponga que una función debe acceder a una variable definida en main(), ¿Cómo se puede lograr?
9. ¿Cuál es la finalidad del operador unario de resolución de alcance?
10. Para el siguiente programa, indique el alcance (sea de función, archivo, bloque o prototipo de función) de los siguientes elementos.
a. La variable x de main() b. La variable y de cubo() c. La función cubo() d. La función main()
e. El prototipo de función de cubo()
f. El identificador y del prototipo de función de cubo()
#include <iostream.h>
int cubo(int y);
int main(void) {
int x;
for(x = 1; x <= 10; x++)
cout << cubo(x) << endl;
return 0;
}
int cubo(int y) {
return y * y * y;
}
10. Escriba una declaración para cada uno de los siguientes elementos:
a. El entero contador, que debe mantenerse en un registro. Inicialice contador a 0.
b. La variable de punto flotante ultimoValor, que debe conservar su valor entre llamadas a la función en que se define.
c. El entero externo numero, cuyo alcance debe restringirse al resto del archivo en que se define.
EXAMEN BREVE 31
1. ¿Dónde deberá colocar una constante que tiene un ámbito de archivo en un programa C++? 2. Una variable local tiene ámbito de ______________________.
3. La modificación de una variable global por medio de una función se conoce como ___________________.
4. ¿Cómo puede retener el valor de una variable local de una función desde una llamada a la función a la siguiente?
5. Los especificadores de clase de almacenamiento son __________, __________, ________ y ___________.
6. Las variables declaradas en un bloque o en la lista de parámetros de una función se toman como de la clase de almacenamiento _____________ a menos que se especifique algo diferente.
7. El especificador de clase de almacenamiento _________ es una recomendación al compilador para que almacene una variable en alguno de los registros de su computadora.
8. Las variables declaradas fuera de cualquier bloque o función son variables __________.
9. Para que las variables locales de una función conserven su valor entre las llamadas a función, se deben declarar con el especificador de clase de almacenamiento ________.
10. Los cuatro alcances posibles de los identificadores son _________, __________, _________ y ___________.
11. El ________ permite acceder a una variable global que tenga el mismo nombre que una variable dentro del alcance actual.
RESPUESTA EXAMEN BREVE 31
1. En un programa C++, una constante que tenga ámbito de archivo deberá colocarse fuera y antes que la función main()
2. Una variable local tiene ámbito de bloque
3. La modificación de una variable global por medio de una función se conoce como efecto lateral.
4. Para conservar el valor de una variable local de función de una llamada a la función a la siguiente, se deberá usar la palabra reservada static frente a la declaración de la variable.
5. Los especificadores de clase de almacenamiento son auto, register, extern y static.
6. Las variables declaradas en un bloque o en la lista de parámetros de una función se toman como de la clase de almacenamiento auto a menos que se especifique algo diferente.
7. El especificador de clase de almacenamiento register es una recomendación al compilador para que almacene una variable en alguno de los registros de su computadora.
8. Las variables declaradas fuera de cualquier bloque o función son variables externas o globales.
9. Para que las variables locales de una función conserven su valor entre las llamadas a función, se deben declarar con el especificador de clase de almacenamiento static.
10. Los cuatro alcances posibles de los identificadores son alcance de función, alcance de archivo, alcance de bloque y alcance de prototipo de función.
11. El operador unario de resolución de alcance permite acceder a una variable global que tenga el mismo nombre que una variable dentro del alcance actual.