Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Regla 0x4009h: Prohibición de anidar llamadas a fopen() directamente dentro de funciones de E/S

Archivos y E/S (0x40XX)

Universidad Nacional de Río Negro

0x4009h: Prohibición de anidar llamadas a fopen() directamente dentro de funciones de E/S

Enunciado normativo

NO DEBE pasarse el valor de retorno de fopen (o freopen) directamente como argumento de otra función de E/S. La apertura DEBE almacenarse en una variable para poder comprobar NULL, usarla y cerrarla.

fscanf(fopen(...), ...) no es una abreviación aceptable: es una apertura sin control.

¿Por qué existe esta regla?

El problema

fopen retorna FILE * y puede retornar NULL. Cuando ese puntero se pasa directamente como argumento, el valor se consume dentro de la expresión y el programa pierde dos oportunidades: no puede comprobar si la apertura falló y no conserva el puntero para cerrarla después.

El primer problema deriva en comportamiento indefinido: la función interna recibe NULL y lo desreferencia. El segundo produce una fuga de recursos: el flujo existe —si la apertura tuvo éxito— pero no hay forma de llamar a fclose sobre él, porque el único puntero quedó enterrado en una expresión ya evaluada. Aunque el programa no falle hoy, cada ejecución de esa línea deja un descriptor abierto.

La regla es, en el fondo, una aplicación de la secuencialidad explícita: cada operación con nombre propio en su propia sentencia, para poder verificarla y reutilizarla (0x0002h: Una declaración de variable por línea).

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Comportamiento indefinidoDesreferenciar el NULL de un fopen fallido produce SIGSEGV.
Fuga de recursosEl flujo exitoso nunca se cierra porque su puntero se perdió.
Diagnóstico imposibleNo hay variable sobre la cual reportar la ruta ni la causa.
LegibilidadLa expresión mezcla apertura y operación, ocultando dos efectos.

Fundamento en el estándar y en la cátedra

ISO/IEC 9899:2011 §7.21.5.3 condiciona la validez del flujo al retorno no nulo de fopen, y §7.21.5.1 exige fclose para liberarlo. La cátedra prohíbe el anidamiento porque hace imposible cumplir 0x4001h: Manejá correctamente la apertura y cierre de archivos y 0x4004h: Mantené la simetría de recursos al abrir y cerrar archivos en el mismo nivel de abstracción en la misma sentencia, y porque el descriptor perdido es la causa más común de fallos intermitentes por agotamiento de recursos.

Alcance y excepciones

Aplica a cualquier llamada de E/S que reciba un FILE *: fread, fwrite, fgetc, fputc, fgets, fputs, fprintf, fscanf, fseek, ftell, feof, fclose. Tampoco se admite fclose(fopen(...)), que abre y cierra sin poder detectar el fallo de apertura ni el de cierre.

Excepción razonable: una función auxiliar puede encapsular la apertura y retornar el FILE *; es correcta si el llamador lo guarda y lo verifica. La prohibición es sobre el anidamiento en la expresión, no sobre las funciones que devuelven flujos.

Ejemplos exhaustivos

❌ Contraejemplo 1 — fscanf con fopen anidado

fscanf(fopen("data.txt", "r"), "%d", &x);

Por qué falla: si "data.txt" no existe, la función recibe NULL y lo desreferencia. Si existe, el flujo queda abierto para siempre porque el puntero no se guardó. No hay forma de saber cuál de los dos casos ocurrió.

❌ Contraejemplo 2 — fprintf y fclose anidados

fprintf(fopen("log.txt", "a"), "inicio\n");

Por qué falla: además de la apertura sin verificar, el búfer puede no volcarse porque nadie cierra el flujo. El mensaje “inicio” quizá nunca llegue al archivo, y el descriptor filtrado se acumula en cada ejecución.

✅ Ejemplo conforme 1 — Variable, verificación, uso y cierre

#include <stdio.h>

FILE *f = fopen("data.txt", "r");
if (f == NULL) {
    perror("data.txt");
    return -1;
}
if (fscanf(f, "%d", &x) != 1) {
    perror("fscanf");
}
fclose(f);

Cada paso tiene su propia sentencia: se comprueba la apertura, se usa el flujo y se cierra. El retorno de fscanf también se verifica (0x4002h: Validá los retornos de las operaciones de lectura y escritura de archivos).

✅ Ejemplo conforme 2 — Función constructora con verificación en el llamador

FILE *abrir_entrada(const char *ruta)
{
    FILE *f = fopen(ruta, "r");
    if (f == NULL) {
        perror(ruta);
    }
    return f;
}

FILE *f = abrir_entrada("data.txt");
if (f == NULL) {
    return -1;
}
int x;
if (fscanf(f, "%d", &x) == 1) {
    procesar(x);
}
fclose(f);

La apertura se encapsula, pero el puntero sigue siendo visible y verificable en el llamador, que además es el dueño responsable del cierre (ver 0x4004h: Mantené la simetría de recursos al abrir y cerrar archivos en el mismo nivel de abstracción).

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cRegla 0x4009h: fopen/freopen dentro de otra llamada.
grepgrep -n 'f.*(fopen' archivo.cApertura anidada en la lista de argumentos.
cppcheckcppcheck --enable=all archivo.cresourceLeak / nullPointer en la expresión.

Checklist de autocontrol

Reglas relacionadas