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 0x4002h: Validá los retornos de las operaciones de lectura y escritura de archivos

Archivos y E/S (0x40XX)

Universidad Nacional de Río Negro

0x4002h: Validá los retornos de las operaciones de lectura y escritura de archivos

Enunciado normativo

DEBE comprobarse el valor de retorno de toda operación de E/S (fread, fwrite, fgetc, fgets, fprintf, fscanf) antes de asumir que la transferencia se completó.

El retorno no es decorativo: es el único canal por el que la biblioteca informa cuántos elementos se transfirieron, si hubo error y si se llegó al fin de archivo.

¿Por qué existe esta regla?

El problema

Las funciones de E/S de C no lanzan excepciones ni abortan el programa ante un error: informan por valor de retorno. fread devuelve la cantidad de elementos efectivamente leídos, que puede ser menor a la pedida; fgetc devuelve EOF; fgets devuelve NULL; fprintf devuelve un valor negativo si falla la escritura.

Un archivo puede terminar antes de lo previsto, un dispositivo puede fallar a mitad de una transferencia o el disco puede llenarse. Si el programa ignora el retorno, sigue usando un búfer que nunca se llenó, con datos viejos o basura, y el defecto se propaga silenciosamente. En escritura, un retorno negativo ignorado hace creer que los datos se guardaron cuando en realidad se perdieron. En E/S binaria una lectura corta puede ser legítima (último bloque), pero debe distinguirse de un error real; para eso están el conteo devuelto y ferror.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Bug silenciosoUn búfer parcialmente leído se procesa como si estuviera completo.
Datos corruptosUn fwrite fallido ignorado produce archivos truncados o incompletos.
Lazo infinitoSi una lectura falla sin marcar EOF y no se mira el retorno, el lazo gira sin avanzar.
Diagnóstico perdidoSin mirar el retorno no se sabe si el problema fue EOF, error de disco o permisos.

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

ISO/IEC 9899:2011 §7.21.8.1 define que fread retorna el número de elementos leídos, menor al pedido solo si ocurre un error o el fin de archivo. §7.21.7.2 fija que fgets retorna NULL ante EOF o error y §7.21.6.4 que fprintf retorna negativo si falla la salida. La cátedra adopta la regla como base de la E/S robusta: sin retornos verificados, ni 0x4003h: Utilizá errno, perror y strerror para reportar fallos del sistema operativo de manera precisa ni 0x4008h: Validación obligatoria del valor de retorno de fclose() en modo escritura pueden funcionar.

Alcance y excepciones

Aplica a las funciones de <stdio.h>: fread, fwrite, fgetc, fgets, fputc, fputs, fprintf, fscanf y fflush. No alcanza a printf/scanf sobre consola cuando el flujo es interactivo y el resultado no compromete datos persistentes, aunque verificarlos sigue siendo buena práctica.

Ejemplos exhaustivos

❌ Contraejemplo 1 — Lectura única sin verificar el conteo

while (!feof(f)) {
    fread(&elem, sizeof(elem), 1, f);
    procesar(elem);
}

Por qué falla: feof no se vuelve verdadero hasta que una lectura previa ya falló. El último elemento se procesa dos veces y, si fread leyó cero por un error distinto de EOF, procesar trabaja sobre datos viejos. Es el antipatrón canónico 0x4006h: Prohibición del antipatrón while (!feof(f)) para control de fin de archivo.

❌ Contraejemplo 2 — Escritura cuyo retorno negativo se descarta

void guardar_todo(FILE *f, const int *valores, size_t n)
{
    for (size_t i = 0; i < n; i++) {
        fprintf(f, "%d\n", valores[i]);
    }
}

Por qué falla: si el disco se llena a mitad del lazo, fprintf devuelve un valor negativo y las líneas restantes no se escriben. El llamador no recibe señal alguna y el archivo queda truncado sin que nadie lo note (ver 0x4008h: Validación obligatoria del valor de retorno de fclose() en modo escritura).

✅ Ejemplo conforme 1 — Lazo controlado por el retorno

int sumar_enteros(const char *ruta, long *total)
{
    FILE *f = fopen(ruta, "r");
    if (f == NULL) {
        return -1;
    }
    *total = 0;
    int x;
    int leidos = 0;
    while (fscanf(f, "%d", &x) == 1) {
        *total += x;
        leidos++;
    }
    fclose(f);
    return leidos;
}

El lazo termina exactamente cuando fscanf deja de convertir un entero, sin duplicar el último dato y sin depender de feof (ver 0x4006h: Prohibición del antipatrón while (!feof(f)) para control de fin de archivo).

✅ Ejemplo conforme 2 — Escritura verificada elemento por elemento

int escribir_bloque(FILE *f, const unsigned char *datos, size_t n)
{
    size_t escritos = fwrite(datos, 1, n, f);
    if (escritos != n) {
        return -1;
    }
    return 0;
}

Comparar escritos con n detecta tanto un error de disco como una transferencia parcial; el llamador puede decidir reintentar o abortar.

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cRetorno de E/S descartado o feof en la condición.
gccgcc -Wall -Wextra -std=c11 archivo.cwarn_unused_result en funciones anotadas.
cppcheckcppcheck --enable=all archivo.cignoredReturnValue.

Checklist de autocontrol

Reglas relacionadas