TransWikia.com

¿Por qué genera un bucle infinito si se introduce una letra en lugar de un numero?

Stack Overflow en español Asked on December 18, 2021

Tengo un problema, quiero hacer que el usuario ingrese un dígito del 1.00 al 10.00 y que si ingresa un dígito fuera de ese rango le mencione que se ha equivocado y lo intente de nuevo. Creo yo que lo he hecho bien pero el problema es que si el usuario ingresa una letra, el bucle se repite de forma infinita, agradecería su ayuda. Gracias. 🙂

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int main()
{
    float a;

    do
    {
        printf("Ingresa tu calificacion de matematicas (1.00 - 10.00):n");
        scanf("%f", &a);

        if (a < 1.00 || a > 10.0)
        {
            printf("Ingresa una calificación validan");
        }
    } while (a < 1.00 || a > 10.00);

    system("pause");
    return 0;
}

2 Answers

¿Por qué genera un bucle infinito si se introduce una letra en lugar de un numero?

Cuando esta sentencia se ejecute:

scanf("%f", &a);

El programa se pausa a la espera de un dato, pero si el usuario ingresa la letra H, la lectura fallará, debido a que, solo se puede leer valores flotantes.

Entonces el búfer internamente quedó así:

Hn

Claro, el búfer quedó con el salto de línea por el ENTER que presionó el usuario.

Luego, la función scanf le asigna a la variable a un 0 (ya que no pudo leer ningún dato) y la condición a < 1.00 || a > 10.0 se cumple, generando que el bucle genere otra iteración. Pero en este caso scanf no pausará el programa porque el búfer del teclado está sucio, por lo tanto, la lectura vuelve a fallar y la variable a sigue quedando con el valor 0 y otra vez se repite el mismo proceso anterior, generando un bucle infinito. Este es el motivo del porque ocurre este problema.

Para solucionar este problema debemos saber que en este caso la función scanf retornará 0 si la lectura falló y con esto podemos detectar el error. Además, tendremos que limpiar el búfer del teclado para asegurar que la próxima lectura se pueda llevar a cabo.

¿Cómo limpiamos el búfer del teclado?

Con la función fflush. ¡No!, esta función no limpia el búfer stdin, sino, el stdout. Por más que funcione en algunos casos, no se lo debe usar. Por ejemplo, en Linux no funcionará esta sentencia (no tendrás el resultado esperado):

fflush(stdin);

Así que lo ideal es usar una solución estándar (que funciona en cualquier lado), como por ejemplo:

int ch;
while((ch = getchar()) != 'n' && ch != EOF);

La función getchar retorna el siguiente caracter que se encuentre en el búfer del teclado.

Ahora, para plantear la solución final debemos dividir el problema en subproblemas. Por ejemplo, podemos crear una función llamada clearBuf (sin parámetros) en la cual se encargue de limpiar el búfer stdin y otra función denominada validFormat para que valide la entrada del usuario.

La primera función quedaría así:

void clearBuf()
{
    int ch;
    //Limpia el búfer del teclado hasta encontrar un salto de línea o fin del archivo
    while((ch = getchar()) != 'n' && ch != EOF);
}

Y la segunda función tendrá dos parámetros y retornará 1 si el usuario no cumple con el formato, de lo contrario, devuelve 0.

int validFormat(const char* fm, void* a)
{
    //Si la función scanf retorna 0, fue porque hubo una falla en la lectura..
    if(!scanf(fm, a))
    {
        //Limpiamos el búfer del teclado para que la próxima lectura se lleve a cabo.
        clearBuf();
        return 1;
    }
    //Si no encontramos un salto de línea en el búfer, es porque el usuario no cumplió con el formato.
    if(getchar() != 'n')
    {
        //Volvemos a limpiar el búfer para la próxima lectura.
        clearBuf();
        return 1;
    }
    return 0;
}

Claro, la función validFormat servirá para leer únicamente una entrada y valida si el usuario cumple con el formato.

Es más, si el usuario intentara ingresar por ejemplo:

23.fd

La función scanf solo tomará la parte entera que en este caso sería 23 y el resto de caracteres se quedarán en el búfer. Sin embargo, la función validFormat lo detectará con la condición: getchar() != 'n' y retornará 1 y esto significa que hubo un error.

Esta función también sirve para validar enteros, por esa razón el segundo parámetro es un puntero genérico.

La solución completa quedaría así:

//Cabeceras:
#include <stdio.h>
#include <stdlib.h>

//La declaración de cada función:
void clearBuf(void);
int validFormat(const char*, void*);

int main(void)
{
    
    float a;
    int result;
    do
    {
        printf("Ingresa tu calificacion de matematicas (1.00 - 10.00):n");
        //Si la función retorna 1 fue porque el usuario no respetó el formato.
        if(validFormat("%f", &a))
        {
            printf("Error: No puedes ingresar letras!n");
            //La sentencia continue hace que pasemos a la próxima iteración.
            continue;
        }
        //Guardamos el resultado de la condición en la variable.
        result = a < 1.00 || a > 10.00;
        if (result) //Equivalente a (result != 0)
            printf("Error: Ingrese una calificacion valida!n");
    } while(result); //Equivalente a (result != 0)
    return 0;
}

int validFormat(const char* fm, void* a)
{
    //Si la función scanf retorna 0, fue porque hubo una falla en la lectura..
    if(!scanf(fm, a))
    {
        //Limpiamos el búfer del teclado para que la próxima lectura se lleve a cabo.
        clearBuf();
        return 1;
    }
    //Si no encontramos un salto de línea en el búfer, es porque el usuario no cumplió con el formato.
    if(getchar() != 'n')
    {
        //Volvemos a limpiar el búfer para la próxima lectura.
        clearBuf();
        return 1;
    }
    return 0;
}

void clearBuf()
{
    int ch;
    //Limpia el búfer del teclado hasta encontrar un salto de línea o fin del archivo
    while((ch = getchar()) != 'n' && ch != EOF);
}

Answered by MrDave1999 on December 18, 2021

Este problema es muy frecuente cuando inicias a programar... básicamente lo que hay que hacer es leer bien que hace la función scanf adicionalmente tener muy en cuenta que es lo que retorna.

La funciona scanf retorna el numero de caracteres leídos de acuerdo a la máscara definida, en tu caso la función debería retornar 0, por que los valores leídos no concuerdan con la máscara, en este caso leer un carácter no concuerda con lo esperado y retorna 0.

leido = scanf("%f", &a);
if (a < 1.00 || a > 10.0 || leido == 0)
{
  fflush(stdin);
  printf("Ingresa una calificación validan");
}

Entonces ya tienes una forma de validar si la entrada leída esta OK, ahora es necesario para evitar el bucle limpiar el flujo de entrada, esto lo puedes lograr con la función fflush, si no se hace no importa que hayas podido identificar que no te digitaron un número, la función scanf seguirá fallando y produciendo el bucle infinito.

Answered by Jonnathan Q on December 18, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP