Regla 0x4008h: Validación obligatoria del valor de retorno de fclose() en modo escritura
Archivos y E/S (0x40XX)
0x4008h: Validación obligatoria del valor de retorno de fclose() en modo escritura¶
Enunciado normativo¶
DEBE verificarse que
fcloseretorne distinto deEOFal cerrar un flujo abierto para escritura ("w","a","wb","ab"o sus variantes de actualización). Un retornoEOFDEBE reportarse como error.
El cierre de un flujo de salida no es una formalidad: es la última operación que puede fallar y la única que garantiza que los datos llegaron al disco.
¿Por qué existe esta regla?¶
El problema¶
stdio trabaja con búferes. Al llamar a fprintf o fwrite, los datos suelen
quedar en memoria y no se escriben de inmediato en el dispositivo. Recién cuando
el búfer se llena, cuando se llama a fflush o cuando se cierra el flujo con
fclose, el sistema intenta la escritura física.
Eso significa que una escritura puede tener éxito a nivel de biblioteca y fallar
después, en el vaciado final. El caso típico es el disco lleno (ENOSPC) o un
error de E/S (EIO) que se manifiesta recién en fclose. Si el programa ignora
el retorno, cree que guardó todo cuando en realidad perdió datos. El error es
silencioso: no hay excepción, no hay mensaje, solo un archivo truncado.
La única forma de detectar esa clase de fallo es mirar el valor de retorno de
fclose, reportarlo y decidir si hay que reintentar o abortar.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Pérdida de datos | El archivo queda incompleto y el programa lo reporta como éxito. |
| Bug silencioso | Un disco lleno no genera ningún aviso; el usuario confía en datos que no existen. |
| Estado inconsistente | Un archivo parcialmente escrito puede corromper ejecuciones posteriores. |
Fundamento en el estándar y en la cátedra¶
ISO/IEC 9899:2011 §7.21.5.1 define fclose: retorna cero si tiene éxito y EOF
si detecta un error, y descarga cualquier dato aún no escrito. La cátedra adopta
la regla porque el trabajo con archivos de salida es parte central de la materia
y la pérdida silenciosa de datos es el peor defecto posible en una entrega.
Alcance y excepciones¶
Aplica a todo fclose de un flujo abierto para escritura o actualización. Para
flujos de solo lectura la verificación es menos crítica, pero sigue siendo buena
práctica por uniformidad.
Excepción razonable: cerrar stdin/stdout/stderr al final del proceso
puede omitir la verificación si el programa no depende de esa salida para su
corrección.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Retorno de fclose descartado¶
FILE *f = fopen("log.txt", "w");
if (f == NULL) {
return -1;
}
fprintf(f, "resultado: %d\n", total);
fclose(f);
return 0;Por qué falla: si el disco se llenó, fprintf pudo haber dejado los datos en el
búfer y fclose falla al vaciarlo. El programa retorna éxito y el archivo de
log queda vacío o truncado sin que nadie lo advierta.
❌ Contraejemplo 2 — Retorno anticipado que saltea el cierre verificado¶
int guardar(const char *ruta, int valor)
{
FILE *f = fopen(ruta, "w");
if (f == NULL) {
return -1;
}
if (valor < 0) {
return -2; /* f no se cierra ni se verifica */
}
if (fprintf(f, "%d\n", valor) < 0) {
return -3; /* tampoco cierra */
}
return fclose(f) == EOF ? -4 : 0;
}Por qué falla: dos ramas retornan sin cerrar el flujo, con lo que además de no verificar el vaciado se fuga el descriptor (ver 0x4004h: Mantené la simetría de recursos al abrir y cerrar archivos en el mismo nivel de abstracción).
✅ Ejemplo conforme 1 — Verificar el cierre y reportar la causa¶
#include <stdio.h>
FILE *f = fopen("log.txt", "w");
if (f == NULL) {
perror("log.txt");
return -1;
}
if (fprintf(f, "resultado: %d\n", total) < 0) {
perror("fprintf");
}
if (fclose(f) == EOF) {
perror("fclose");
return -1;
}
return 0;El retorno de fclose se compara con EOF y, ante fallo, se reporta con
perror conservando la causa del sistema (0x4003h: Utilizá errno, perror y strerror para reportar fallos del sistema operativo de manera precisa).
✅ Ejemplo conforme 2 — fflush explícito antes de cerrar¶
if (fwrite(datos, 1, n, f) != n) {
perror("fwrite");
}
if (fflush(f) == EOF) {
perror("fflush");
}
if (fclose(f) == EOF) {
perror("fclose");
}Forzar el vaciado con fflush separa el error de escritura del de cierre y hace
que el diagnóstico apunte a la operación concreta que falló.
⚠️ Casos límite¶
Solo lectura: un
fcloseque falla sobre un flujo de lectura no pierde datos, pero puede indicar un problema del descriptor; verificalo igual.Doble cierre: verificar el retorno no justifica cerrar dos veces; el segundo
fclosees comportamiento indefinido (ver 0x400Ah: Prohibición de operar sobre flujos de archivo tras haber invocado fclose() (use-after-close)).fclosetras fallo de escritura: igualmente hay que cerrar para liberar el descriptor y comprobar si el vaciado también falló.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Regla 0x4008h: fclose sin comparación contra EOF. |
gcc | gcc -Wall -Wextra -std=c11 archivo.c | -Wunused-result si se ignora el retorno. |
| Revisión manual | — | Todo fclose de un flujo "w"/"a" debe consumir su resultado. |
Checklist de autocontrol¶
¿Comparé el retorno de
fcloseconEOF?¿Reporté el fallo con
perrorsobre el flujo de escritura?¿Cerré el flujo en todos los caminos, incluidos los de error?
Reglas relacionadas¶
0x4001h: Manejá correctamente la apertura y cierre de archivos — cerrar el recurso y verificar la apertura son obligaciones pareadas.
0x4002h: Validá los retornos de las operaciones de lectura y escritura de archivos — el retorno de
fwrite/fprintfse verifica aparte.0x4003h: Utilizá errno, perror y strerror para reportar fallos del sistema operativo de manera precisa —
perror/strerrorpara reportar el fallo de cierre.