Ámbito de variables en C (scope)
En articulos anteriores hemos visto la declaración de variables y constantes en C. Ya tenemos todo lo necesario para entender qué es el ámbito de las variables, que en esencia significa que ciertas variables estarán disponibles únicamente en cierta parte de nuestro código.
En C podemos clasificar las variables como privadas, estáticas y externas. Esta clasificación se conoce como Clases de almacenamiento también. Las variables privadas son las que están definidas dentro de un bloque de código, por lo cual, tiene alcance interno. Las variables estáticas y externas tienen alcance tanto interno como a nivel global.
Además de esta clasificación podemos agregar la clase de almacenamiento register, la cual es una recomendación al compilador para que almacene dicha variable, si es posible, en un registro de la máquina. Si no es posible guardarla como registro se guadará como automática (privada). Este tipo de almacenamiento solo está disponible para variables de tipo entero o punteros.
Tenga en cuenta la siguiente tabla para saber si una clase de almacenamiento puede usarse a nivel interno o de bloque, a nivel global o como argumento de una función.
interno | global | argumento | |
---|---|---|---|
auto | X | ||
static | X | X | |
extern | X | X | |
register | X | X |
Variables privadas (por defecto)
Las variables privadas solamente estarán disponibles en la función o bloque en el cual fueron definidas. No se puede definir una variable privada a nivel global, sería entonces estática o externa. Cuando se declara una variable dentro de un bloque y no se indica su ámbito se asume que es privada por defecto. Para definir una variable como privada basta utilizar el modificador auto
, el cual, sería redundante ya que como se dijo es el comportamiento por defecto.
#include <stdio.h>
void define_pi();
int main()
{
int pi;
define_pi();
printf("PI is %d\n", pi);
return 0;
}
void define_pi()
{
auto int pi = 3.1416;
}
Cuando ejecutamos este programa veremos un valor de PI cada vez diferente. Esto sucede porque la variable pi
que está definida dentro de la función pi()
no es visible dentro de la función main()
. Nota que pi también está declarada en la función main() pero no inicializada, y esta es la razón por la cual obtenemos el valor aleatorio.
Otro caso ocurre cuando definimos variables dentro de un bloque e intentamos utilizarlas fuera de él. El siguiente programa siempre va a arrojar error ya que la variable version existe solo a nivel de bloque.
#include <stdio.h>
int main()
{
if (1) {
int version = 1;
} else {
int version = 0;
}
printf("The version for this file is: %d\n", version);
return 0;
}
Variables estáticas
Las variables estáticas utilizan el modificador static
antes del tipo de variable para indicar que es de tipo estática.
static int version = 1;
A nivel global
A nivel global una variable estática hace que las variables sean visibles solamente dentro de su propio archivo fuente.
main.c
#include <stdio.h>
static int version = 1;
int main()
{
printf("The version for this file is: %d\n", version);
return 0;
}
variables.c
int version = 1;
Este programa debe ser compilado de la siguiente manera.
gcc main.c variables.c
La variable version
en el archivo main utiliza solamente la referencia local. Si esta variable no fuera definida como static el compilador lanzaría un error porque tenemos dos definiciones de la misma variable a nivel global.
A nivel interno
Este tipo de variables a nivel interno permiten "guardar" la inicialización de un valor en memoria. Veamos el siguiente ejemplo:
#include <stdio.h>
int add(int x)
{
static int sum = 0;
sum += 5;
return sum;
}
int main()
{
printf("The sum is: %d\n", add(5));
printf("The sum is: %d\n", add(5));
return(0);
}
La salida de este programa es:
The sum is: 5
The sum is: 10
Parece extraño, pero todo radica en la ejecución de la línea
static int sum = 0;
La primera vez que se ejecuta se comporta como se espera, define la variable sum con valor cero. La segunda vez que se ejecuta la función asume que la variable ya fue definida en la primera ejecución, por lo cual ya viene con el último valor que tenía que es cinco.
Sin embargo, los acumuladores como el ejemplo anterior no son la única utilidad de esta clase de almacenamiento en C. Tmabién es útil para devolver valores como cadenas de caracteres que necesitan persistir ya que como verás en Arrays en C, las cadenas son en realidad se pasan como punteros en las funciones. Para ver este ejemplo más a detalle puedes ver nuestro artículo Variables estáticas con strings en C.
Variables externas
Las variables externas utilizan el modificador extern
antes del tipo de variable para indicar que es de tipo externa.
extern float pi;
A nivel global
Las variables externas tiene un significado muy profundo en lo relativo a aplicaciones a gran escala. En esencia permite referenciar una variable que ha sido creada en otro archivo a nivel global. Veamos el siguiente ejemplo.
main.c
#include <stdio.h>
extern float pi;
int main()
{
printf("PI is %.4f\n", pi);
return 0;
}
mylib.c
float pi = 3.1516;
Nota que ninguno de estos dos archivos referencia al otro con un include. Para compilar este programa debemos indicarle al compilador cada uno de estos archivos de la siguiente manera.
gcc main.c mylib.c
Nota que aunque la variable pi fue creada a nivel global en mylib.c debe ser referenciada en el archivo principal. También puedes utilizar archivos de cabecera para abstrar un poco más el código y crear librerías. Esto lo puedes ver un poco más a fondo en nuestro artículo compilación de programas en C.
A nivel interno
A nivel interno el modificador extern
hace visible una variable que no ha sido todavía definida.
#include <stdio.h>
extern int version;
int main()
{
printf("The version for this file is: %d\n", version);
return 0;
}
int version = 1;