scanf falla ¿por qué?

cuando escribí esto, comstackr y ejecutar:

int x; scanf ("%d", &x); while (x!=4) { scanf ("%d", &x); } 

y cuando inserte un número doble o un número doble menor que 4, ingrese un ciclo infinito.
cuando inserta doble mayor que 4, termina.
¿Alguna explicación?

Del estándar de lenguaje C (n1256) :

7.19.6.2 La función fscanf

4 La función fscanf ejecuta cada directiva del formato sucesivamente. Si falla una directiva, como se detalla a continuación, la función regresa. Las fallas se describen como fallas de entrada (debido a la ocurrencia de un error de encoding o la falta de disponibilidad de caracteres de entrada) o fallas de coincidencia (debido a una entrada inapropiada).

7 Una directiva que es una especificación de conversión define un conjunto de secuencias de entrada coincidentes, como se describe a continuación para cada especificador. Una especificación de conversión se ejecuta en los siguientes pasos:

8 Los caracteres de espacio en blanco de entrada (según lo especificado por la función isspace) se omiten, a menos que la especificación incluya un especificador [ , c , o n . 250)

9 Un elemento de entrada se lee de la secuencia, a menos que la especificación incluya un n especificador. Un elemento de entrada se define como la secuencia más larga de caracteres de entrada que no supera ningún ancho de campo especificado y que es, o es un prefijo de, una secuencia de entrada coincidente. 251) El primer carácter, si lo hay, después del elemento de entrada permanece sin leer. Si la longitud del elemento de entrada es cero, la ejecución de la directiva falla; esta condición es una falla de coincidencia a menos que el fin de archivo, un error de encoding o un error de lectura hayan impedido la entrada de la secuencia, en cuyo caso se trata de una falla de entrada.

10 Excepto en el caso de un especificador % , el elemento de entrada (o, en el caso de una directiva % n , el recuento de caracteres de entrada) se convierte a un tipo apropiado para el especificador de conversión. Si el elemento de entrada no es una secuencia coincidente, la ejecución de la directiva falla: esta condición es una falla coincidente. A menos que la supresión de asignación se indique con un *, el resultado de la conversión se coloca en el objeto al que apunta el primer argumento que sigue al argumento de formato que aún no ha recibido un resultado de conversión. Si este objeto no tiene un tipo apropiado, o si el resultado de la conversión no se puede representar en el objeto, el comportamiento no está definido.

Énfasis añadido en el párrafo 10. El especificador de conversión %d espera que el texto de entrada se formatee como un entero decimal. Si no lo es, la conversión falla y el carácter que causó la conversión falla en el flujo de entrada. Las llamadas adicionales a scanf() con el especificador de conversión %d se obstruirán con el mismo carácter.

scanf() devuelve el número de asignaciones exitosas; necesita verificar este resultado para ver si la conversión tuvo éxito, así:

 int x = 0; while (x != 4) { int result = scanf("%d", &x); if (result != 1) { printf("Last call to scanf() failed; exiting\n"); break; } } 

Lamentablemente, todavía tiene la mala entrada atascada en la secuencia de entrada. Hay una serie de estrategias para lidiar con esto. Puede eliminar el carácter ofensivo con getchar y volver a intentarlo:

 while (x != 4) { int tmp; if (scanf("%d", &tmp) == 0) getchar(); else x = tmp; } 

O puede intentar leer hasta la siguiente línea nueva, suponiendo que se ha procesado toda la información restante:

 while (x != 4) { int tmp; if (scanf("%d", &tmp) == 0) while (getchar() != '\n') ; else x = tmp; } 

O podría tratar de leer la entrada como texto y convertir a un entero usando strtol() (mi técnica preferida):

 char input[SOME_SIZE]; int x = 0; ... while (x != 4) { if (fgets(input, sizeof input, stdin)) { char *check; int tmp = (int) strtol(input, &check, 10); if (!isspace(*check) && *check != 0) { printf("%s is not a valid integer: try again\n", input); } else { x = tmp; } } else { printf("Read error on standard input\n"); break; } } 

Es más trabajo, pero le permite detectar los datos erróneos antes de asignarlos a x .

No verifica si el scanf realmente tuvo éxito, por lo tanto, se quedará atascado en el error. Con cada bucle, el scanf intentará leer y fallará.

scanf devuelve el número de elementos leídos con éxito, por lo tanto, modifique el bucle a algo así while (x!=4) { if (scanf("%d",&x) != 1) break; } while (x!=4) { if (scanf("%d",&x) != 1) break; }

Cuando scanf detiene el escaneo en una posición específica en la secuencia de entrada, nunca avanzará la transmisión, por lo que el siguiente escaneo intentará nuevamente el mismo error … y nuevamente … y nuevamente

 entrada: 42 23 foo ...
 scanf: ^
 x 42
 scanf: ^
 x 23
 scanf: ^
 x inutilizable
 scanf: ^
 x inutilizable
 scanf: ^
 x inutilizable
 scanf: ^
 x inutilizable
 scanf: ^
 x inutilizable

El% d que pasó al scanf le dice que analice un int. Cuando ingresa un doble, no puede analizar ni almacenar los datos analizados en la variable x porque el doble usa 64 bits en máquinas de 32 bits e int solo 32 bits, lo que genera un Seg. Fault o efectos colaterales aleatorios.