Regla 0x2004h: No se permite el uso de variables globales
Funciones, contratos y modularizacion (0x20XX)
0x2004h: No se permite el uso de variables globales¶
Enunciado normativo¶
NO DEBE declararse estado mutable con alcance de archivo ni con enlace externo. Los datos compartidos DEBEN viajar como parámetros, o bien encapsularse dentro de un tipo y pasaarse un puntero a ese tipo.
¿Por qué existe esta regla?¶
El problema¶
Una variable global es una entrada y una salida invisible de cada función: la
firma promete depender solo de sus parámetros, pero el resultado también cambia
con modo_global. Cualquier función puede escribirla, así que rastrear quién la
modificó obliga a auditar todo el programa. El orden de las llamadas pasa a
importar de forma oculta y aparecen bugs no reproducibles.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Bug silencioso | Una función pisa un global que otra función esperaba leer intacto. |
| Testing | Los tests no son independientes: dependen del orden de ejecución. |
| Mantenibilidad | Para entender una función hay que leer todo el archivo. |
Fundamento en el estándar y en la cátedra¶
En C11 un objeto declarado fuera de toda función tiene duración estática y vive
mientras dure el programa (§6.2.4); su enlace puede ser interno (static) o
externo (§6.2.2). El estándar lo permite; la cátedra lo prohíbe por el diseño
modular de 0x2005h: Cada función debe tener una única responsabilidad (Principio de Responsabilidad Única). La presente endurece a 0x0106h: Las variables globales deben ser declaradas como static o usar prefijo g_.
Alcance y excepciones¶
Prohíbe el estado mutable: variables que cambian durante la ejecución. Sí se
admiten constantes #define (0x0107h: Las macros #define deben nombrarse en MAYUSCULAS_SNAKE_CASE), objetos const de solo lectura
con enlace interno (static const) y enum de constantes (0x300Dh: Utilizá enum en lugar de ‘números mágicos’ para conjuntos de estados y valores constantes).
Si hace falta estado compartido, se encapsula en un struct y se pasa por
parámetro; el dueño queda explícito en el código.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Contador global compartido¶
int total_procesados = 0;
void registrar(struct item_t *it)
{
if (it->valido == true) {
total_procesados++;
}
}Por qué falla: registrar altera un estado que no figura en su firma. Dos
pruebas consecutivas dan resultados distintos, y cualquier otro módulo puede
leer o pisar total_procesados sin aviso.
❌ Contraejemplo 2 — Configuración global dispersa¶
int modo_debug = 1;
const char *ruta_salida = "salida.txt";
void escribir(const char *texto)
{
if (modo_debug == 1) {
fprintf(stderr, "[debug] %s\n", texto);
}
/* usa ruta_salida directamente */
}Por qué falla: la función depende de dos globales que no recibe ni documenta. No se puede probar en modo silencioso ni redirigir la salida sin tocar el estado del proceso entero.
✅ Ejemplo conforme 1 — Estado encapsulado y pasado por parámetro¶
struct contador_t {
size_t total;
};
void registrar(struct contador_t *c, const struct item_t *it)
{
if (it != NULL && it->valido == true) {
c->total++;
}
}El estado viaja como parámetro. La firma dice exactamente de qué depende y qué modifica; el dueño es quien declaró la variable.
✅ Ejemplo conforme 2 — Constante de configuración inmutable¶
#define BUFFER_MAX 1024
#define RUTA_SALIDA "salida.txt"
static const double PI = 3.141592653589793;
double area_circulo(double radio)
{
return PI * radio * radio;
}PI, BUFFER_MAX y RUTA_SALIDA no cambian durante la ejecución, así que no
son estado compartido. El prefijo static restringe PI a la unidad de
traducción y evita colisiones de enlace.
⚠️ Casos límite¶
constno siempre basta:constsobre el objeto protege la escritura, pero unconstde enlace externo puede colisionar entre módulos; preferístatic const.Punteros constantes:
char *constno es lo mismo queconst char *; según 0x3017h: Orden canónico de calificadores: ‘const tipo’ en lugar de ‘tipo const’ el calificador se escribe antes del tipo.Arreglos globales grandes: aunque sean
const, evalúa si conviene declararlosstaticdentro de la función que los usa.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Variable mutable fuera de función. |
gcc / clang | gcc -Wall -Wextra -Wshadow -std=c11 ... | Uso de un objeto no local sin pasarlo por parámetro. |
| Revisión manual | — | Firma que omite una entrada que la función realmente lee. |
Checklist de autocontrol¶
¿Todo dato que mi función lee o modifica entra por parámetro?
¿Lo que declaré afuera es constante y de solo lectura?
¿Evité depender del orden de llamadas entre funciones?
¿Si necesito estado compartido, lo encapsulé en un
struct?
Reglas relacionadas¶
0x2005h: Cada función debe tener una única responsabilidad (Principio de Responsabilidad Única) — el estado global acopla responsabilidades que deberían estar separadas.
0x0106h: Las variables globales deben ser declaradas como static o usar prefijo g_ — pauta sobre globales de archivo; la presente la endurece.
0x300Dh: Utilizá enum en lugar de ‘números mágicos’ para conjuntos de estados y valores constantes —
enumpara conjuntos de constantes, en lugar de enteros sueltos.0x200Ah: Modularización: una función no debe exceder 4 parámetros de entrada — el estado global suele tapar funciones con demasiados acoplamientos.