Alcance, duración y visibilidad de variables en C

Capítulo 8

Tiempo estimado de lectura: 7 minutos

+ Ejercicio

Conceptos clave: alcance, duración y visibilidad

En C, una variable no solo se define por su tipo y nombre: también importa dónde se puede usar (alcance), cuánto tiempo existe (duración) y desde dónde se puede referenciar (visibilidad o enlace). Distinguir estos conceptos evita errores difíciles de detectar, especialmente cuando el mismo identificador aparece en varios lugares.

  • Alcance (scope): región del código donde el nombre de la variable es válido. Normalmente depende de llaves { } (bloques) o del nivel de archivo.
  • Duración (lifetime / storage duration): cuánto tiempo “vive” la variable durante la ejecución. Puede ser automática (entra/sale con el bloque) o estática (vive todo el programa).
  • Visibilidad (linkage): si un identificador puede ser referenciado desde otros archivos (.c) o solo dentro del archivo actual. Esto se controla principalmente con static a nivel de archivo.
Tipo típicoEjemploAlcanceDuraciónVisibilidad
Local automáticaint x; dentro de una funciónBloqueAutomáticaSolo dentro de la función/bloque
Local estáticastatic int c; dentro de una funciónBloqueEstáticaSolo dentro de la función
Globalint g; fuera de funcionesArchivo (desde la declaración)EstáticaPor defecto externa (otros archivos pueden verla con extern)
Global “privada”static int g; fuera de funcionesArchivoEstáticaSolo dentro del archivo

Variables locales: alcance de bloque y duración automática

Una variable declarada dentro de una función (y sin static) tiene alcance de bloque y duración automática. Esto significa que existe desde que la ejecución entra al bloque hasta que sale de él.

Ejemplo: modificar dentro de un bloque anidado sí afecta fuera (misma variable)

Si no redeclaras la variable, un bloque interno puede modificar la misma variable definida en el bloque externo.

int main(void) {    int x = 10;    {        x = 20;  /* modifica la misma x */    }    /* aquí x vale 20 */    return 0;}

Ejemplo: modificar dentro de un bloque NO afecta fuera (shadowing)

Si en el bloque interno declaras otra variable con el mismo nombre, la variable interna sombras (shadow) a la externa. El resultado típico: crees que modificas la externa, pero en realidad cambias una distinta.

int main(void) {    int x = 10;    {        int x = 20; /* nueva x: sombrea a la externa */        x = 30;     /* modifica la x interna */    }    /* aquí x sigue valiendo 10 */    return 0;}

Este patrón genera bugs sutiles porque el código “parece” correcto a simple vista. En especial, ocurre en bloques de if, for o al introducir variables temporales con nombres genéricos como i, temp o result.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

Guía práctica: cómo detectar y evitar shadowing

Paso 1: identifica el bloque donde se declaró la variable

Ubica la declaración más cercana hacia arriba. En C, el nombre se resuelve buscando en el bloque actual; si no está, se busca en el bloque contenedor, y así sucesivamente.

Paso 2: revisa si hay una redeclaración con el mismo nombre

Si encuentras una declaración del mismo identificador en un bloque interno, la externa queda inaccesible dentro de ese bloque.

Paso 3: aplica una corrección según la intención

  • Si querías modificar la variable externa: no redeclares el nombre en el bloque interno.
  • Si querías una variable temporal: usa un nombre distinto (por ejemplo, x_temp), o limita su uso a una función auxiliar.

Paso 4: apóyate en advertencias del compilador

Activa advertencias para que el compilador te avise de sombras. Por ejemplo, en GCC/Clang suele ayudar -Wall -Wextra -Wshadow. Esto no cambia el comportamiento del programa, pero reduce errores.

Variables globales: alcance de archivo y visibilidad entre archivos

Una variable declarada fuera de cualquier función tiene alcance de archivo (desde su declaración hasta el final del archivo) y duración estática (existe durante toda la ejecución). La diferencia importante es su visibilidad:

  • Sin static a nivel de archivo: la variable tiene, por defecto, visibilidad externa (otros archivos pueden referenciarla mediante extern).
  • Con static a nivel de archivo: la variable queda “privada” del archivo (solo se puede usar en ese .c).

Ejemplo: global accesible desde el mismo archivo

int contador_global = 0;  /* visible en este archivo desde aquí */void inc(void) {    contador_global++;}

Ejemplo: global “privada” del archivo con static

static int contador_archivo = 0;  /* solo visible en este .c */void inc(void) {    contador_archivo++;}

Usar static a nivel de archivo es una técnica común para evitar colisiones de nombres y reducir acoplamiento: otros módulos no pueden depender accidentalmente de esa variable.

static dentro de una función: misma visibilidad, distinta duración

Cuando declaras una variable local con static, su alcance sigue siendo el bloque donde se declara (solo se puede usar allí), pero su duración pasa a ser estática: conserva su valor entre llamadas.

Ejemplo: contador persistente entre llamadas

int siguiente_id(void) {    static int id = 0; /* se inicializa una vez */    id++;    return id;}

Aquí, id no “se reinicia” cada vez que se llama a la función. Esto es útil para contadores, cachés simples o estados internos. También puede ser peligroso si introduces estado oculto que complica pruebas y reutilización.

Ejemplos comparativos: “no afecta fuera” y “sí afecta fuera”

Caso A: no afecta fuera (variable distinta por shadowing)

void demo_shadowing(void) {    int total = 100;    if (total > 0) {        int total = 0;  /* sombrea */        total += 50;   /* cambia el total interno */    }    /* total sigue siendo 100 */}

Caso B: sí afecta fuera (misma variable, sin redeclarar)

void demo_mismo_identificador(void) {    int total = 100;    if (total > 0) {        total += 50;  /* cambia el total externo */    }    /* total ahora es 150 */}

Caso C: “viceversa” con global vs local (la local no afecta a la global)

Si existe una global y dentro de una función declaras una local con el mismo nombre, la local sombrea a la global dentro de la función. Esto suele ser fuente de errores porque parece que estás actualizando el estado global, pero no.

int estado = 0;void set_estado(void) {    int estado = 1; /* sombrea a la global */    estado = 2;     /* modifica la local */    /* la global sigue en 0 */}

Si realmente necesitas modificar la global, no la sombres: usa otro nombre para la local o elimina la redeclaración.

Recomendaciones de estilo: menos globales, dependencias explícitas

Minimiza variables globales

  • Prefiere pasar datos como parámetros y devolver resultados (o usar punteros a estructuras) en lugar de depender de globales.
  • Si necesitas estado compartido dentro de un módulo, prefiere static a nivel de archivo para encapsularlo.
  • Evita nombres genéricos en globales (data, value, flag) porque aumentan el riesgo de colisiones y confusión.

Haz dependencias explícitas en las funciones

Una función es más fácil de probar y reutilizar si su comportamiento depende de sus parámetros, no de variables externas ocultas.

  • En lugar de: void procesar(void) que usa una global config, prefiere: void procesar(const Config* config).
  • Si necesitas “estado” entre llamadas, considera agruparlo en una estructura y pasar un puntero: void step(Estado* e), en vez de usar static local salvo que sea realmente un detalle interno.

Evita shadowing deliberado

  • No reutilices el mismo nombre para variables en bloques anidados.
  • Usa nombres que expresen intención: indice, limite, acumulado, acumulado_parcial.
  • Activa advertencias del compilador para detectar sombras y trátalas como errores en tu flujo de trabajo.

Ahora responde el ejercicio sobre el contenido:

¿Qué efecto tiene declarar una variable con el mismo nombre dentro de un bloque interno en C (shadowing) respecto a la variable del bloque externo?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

En shadowing, una nueva declaración con el mismo nombre en un bloque interno hace que la variable externa quede inaccesible dentro de ese bloque. Las asignaciones modifican la variable interna, y al salir del bloque la externa conserva su valor original.

Siguiente capítulo

Buenas prácticas de estilo en C y lectura efectiva de código

Arrow Right Icon
Portada de libro electrónico gratuitaFundamentos de programación en C desde cero: variables, control de flujo y funciones
73%

Fundamentos de programación en C desde cero: variables, control de flujo y funciones

Nuevo curso

11 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.