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 0x4006h: Prohibición del antipatrón while (!feof(f)) para control de fin de archivo

Archivos y E/S (0x40XX)

Universidad Nacional de Río Negro

0x4006h: Prohibición del antipatrón while (!feof(f)) para control de fin de archivo

Enunciado normativo

NO DEBE usarse feof(f) ni ferror(f) como condición de un lazo de lectura. El lazo DEBE controlarse con el valor de retorno de la operación de lectura (fgets, fread, fscanf, fgetc).

La condición del lazo debe expresar “mientras la lectura tuvo éxito”, no “mientras el indicador de fin de archivo esté apagado”.

¿Por qué existe esta regla?

El problema

feof no anticipa el fin de archivo: lo reporta después. El indicador se activa únicamente cuando una lectura intentó ir más allá del último dato y falló. Por eso while (!feof(f)) es verdadero durante una iteración de más: la lectura final devuelve NULL o cero elementos, pero el cuerpo del lazo se ejecuta igual antes de volver a evaluar la condición.

El resultado clásico es que el último registro se procesa dos veces. Peor aún, si la lectura no devuelve cero por EOF sino por un error real (por ejemplo, un dispositivo que falla), feof nunca se activa y el lazo se vuelve infinito, reprocesando el mismo contenido o basura indefinidamente. Para distinguir EOF de error están feof y ferror, pero se consultan después de que la lectura indicó un problema, no para decidir si leer.

Consecuencias de violarla

Tipo de consecuenciaEfecto concreto
Resultado incorrectoEl último registro se procesa dos veces (duplicado espurio).
Lazo infinitoUn error de lectura distinto de EOF no activa feof y el lazo no termina.
Datos basuraSe procesa un búfer sin actualizar en la iteración extra.
Dependencia frágilEl comportamiento depende de detalles del búfer de stdio, no del algoritmo.

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

ISO/IEC 9899:2011 §7.21.10.2 define feof, que retorna verdadero solo si el indicador de fin de archivo está activo, y el indicador se activa al encontrarse el fin durante una lectura. §7.21.10.3 define ferror como su contraparte de error. La cátedra prohíbe el patrón porque viola el principio de controlar el flujo con el resultado de la operación y porque produce errores silenciosos difíciles de ver en pruebas con pocos datos.

Alcance y excepciones

Aplica a todo lazo de lectura de archivos, sin importar la función usada. No alcanza al uso de feof/ferror después del lazo, que es correcto para decidir si la terminación fue por fin de archivo o por error.

Excepción razonable: ningún uso de feof como cabecera de lazo es aceptable. La forma correcta siempre existe y es más simple.

Ejemplos exhaustivos

❌ Contraejemplo 1 — fgets con feof en la cabecera

while (!feof(f)) {
    fgets(buf, sizeof(buf), f);
    procesar(buf);
}

Por qué falla: en la última iteración fgets devuelve NULL y deja buf sin modificar, pero procesar se ejecuta igual y vuelve a tratar el contenido anterior. Es el antipatrón 0x4006h: Prohibición del antipatrón while (!feof(f)) para control de fin de archivo.

❌ Contraejemplo 2 — fread con feof y error real

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

Por qué falla: si fread falla por un error de E/S distinto de EOF, feof nunca se activa y el lazo gira para siempre. Además el retorno se descarta, con lo que ni siquiera se detecta el problema (0x4002h: Validá los retornos de las operaciones de lectura y escritura de archivos).

✅ Ejemplo conforme 1 — fgets controlado por su retorno

while (fgets(buf, sizeof(buf), f) != NULL) {
    procesar(buf);
}

Cada iteración procesa exactamente la línea que se acaba de leer; cuando se agota el archivo, fgets devuelve NULL y el lazo termina sin una vuelta extra.

✅ Ejemplo conforme 2 — fread controlado por su retorno y error aparte

while (fread(&elem, sizeof(elem), 1, f) == 1) {
    procesar(elem);
}
if (ferror(f)) {
    perror("fread");
}

El lazo se ejecuta una vez por elemento leído. Al salir, feof y ferror distinguen si terminó por fin de archivo o por error, y el diagnóstico se reporta con perror (0x4003h: Utilizá errno, perror y strerror para reportar fallos del sistema operativo de manera precisa).

⚠️ Casos límite

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cRegla 0x4006h: feof/ferror en la condición de un lazo.
gccgcc -Wall -Wextra -std=c11 archivo.cSin señal directa; requiere revisión.
Revisión manualBuscar while (!feof y while (!ferror en el código.

Checklist de autocontrol

Reglas relacionadas

Antipatrón: Control de lectura con while(!feof())

Síntoma en el código del estudiante

Aparece un lazo de lectura cuya condición es while (!feof(f)) (o while (!ferror(f))), y dentro del cuerpo se llama a fgets, fread o fscanf sin usar su valor de retorno para controlar la iteración. El indicador de fin de archivo se trata como si fuera una condición de corte anticipada.

Diagnóstico

Mecanismo del defecto

feof no predice el fin de archivo: lo confirma con retraso. El indicador interno se activa cuando una lectura ya intentó pasar del último dato y falló. Por eso, cuando while (!feof(f)) evalúa verdadero, todavía no se leyó nada, y la iteración de más se ejecuta antes de que la condición vuelva a evaluarse. En esa iteración extra la lectura devuelve NULL o cero elementos, pero el cuerpo se ejecuta igual con el búfer en su estado anterior.

Además, feof es ajeno a los errores. Si la lectura falla por una causa distinta del fin de archivo (un dispositivo que se desconecta, un sector dañado), el indicador de EOF nunca se activa y el lazo no termina: se reprocesa el mismo búfer indefinidamente.

Consecuencia observable

El síntoma clásico es que el último registro se procesa dos veces: se duplica la última línea, el último entero o el último elemento del arreglo. Con suficientes datos, además, el lazo puede no terminar cuando aparece un error de lectura, y la salida del programa deja de avanzar.

Fundamento en el estándar C11

ISO/IEC 9899:2011 §7.21.10.2 define feof como verdadero solo si el indicador de fin de archivo está activo, y ese indicador se activa al encontrarse el fin durante una lectura. §7.21.7.2 establece que fgets retorna NULL ante EOF o error, y §7.21.8.1 que fread retorna el número de elementos leídos, menor al pedido ante error o fin de archivo. El retorno de la lectura, no feof, es la señal de corte correcta.

Corrección idiomática

❌ Código con el antipatrón
while (!feof(f)) {
    if (fgets(buf, sizeof(buf), f) != NULL) {
        procesar(buf);
    }
}

Por qué es incorrecto: la guarda interna evita procesar el búfer nulo, pero el lazo igual da una vuelta de más y la condición sigue dependiendo de feof. Tampoco distingue si la última lectura falló por EOF o por error.

✅ Código refactorizado
while (fgets(buf, sizeof(buf), f) != NULL) {
    procesar(buf);
}

if (ferror(f)) {
    perror("fgets");
}

La condición es el resultado de la lectura: cada iteración procesa exactamente lo que se acaba de leer, sin vueltas extra. Al salir, ferror permite separar un fin de archivo normal de un error real, que se reporta con perror.

Errores típicos al compilar o ejecutar

No hay error de compilación: el defecto es lógico y silencioso. La señal aparece en la salida del programa.

$ ./contar_lineas datos.txt
uno
dos
dos
Total: 3 lineas          <-- datos.txt tenia solo 2 lineas

Con un error de lectura, el síntoma cambia de duplicación a bloqueo.

$ ./leer_archivo disco_danado.bin
leyendo...
leyendo...
leyendo...               <-- no termina: feof nunca se activa

Checklist de verificación

Reglas relacionadas