Comportamiento extraño de scanf al leer número y nueva línea

Me acabo de dar cuenta de este ‘error’ de scanf ahora después de 8 años con C.

El código debajo de scanf omitirá los caracteres de espacio en blanco iniciales de la segunda línea de entrada.

int x; char in[100]; scanf("%d\n",&x); gets(in); 

Entrada:

 1 s 

x contendrá 1 , pero in "s" no " s"

¿Es este estándar C o solo el comportamiento de gcc?

Un carácter de espacio en blanco en la cadena de formato scanf hará que scanf consum cualquier espacio en blanco (y todo) hasta que se produzca un carácter que no sea espacio en blanco.

Este parece ser el comportamiento estándar de scanf y no se limita a gcc.

No es un error en scanf , dice el manual de scanf ,

Una secuencia de caracteres de espacio en blanco (espacio, tabulador, nueva línea, etc.; Vea isspace(3) ). Esta directiva coincide con cualquier cantidad de espacio en blanco, incluyendo ninguno, en la entrada.

Lo que significa que cualquier carácter de espacio en blanco con una directiva como %d\n leerá un número seguido de una secuencia de caracteres de espacio en blanco en la entrada y solo regresará hasta que escriba un carácter de espacio en blanco. Así es como puedes ver solo "s" sin un espacio delante de él.

El '\n' (y esto es cierto para cualquier carácter de espacio en blanco en la cadena de formato) en

 scanf("%d\n", &x); 

coincide con cualquier número de caracteres de espacio en blanco en la entrada (caracteres para los cuales la función de isspace devuelve 1, es decir, verdadero, como nueva línea, espacio, tabulación, etc.) y no solo el carácter de nueva línea '\n' . Esto significa que scanf leerá todos los caracteres de espacio en blanco en la entrada y los descartará hasta que encuentre un carácter que no sea de espacio en blanco. Esto explica el comportamiento que observaste.

Esta es una parte de la definición estándar de la función scanf y no una característica gcc . Además, gets función está en desuso y no es seguro . No comprueba si hay exceso de búfer y puede provocar errores e incluso locking del progtwig. De hecho, gcc emite una advertencia contra el uso de gets en mi máquina. Se recomienda el uso de fgets lugar.

Para hacer lo que quieras, puedes hacer lo siguiente:

 int x; char in[100]; scanf("%d", &x); 

Después de que scanf devuelve con éxito, la secuencia de entrada puede contener cualquier secuencia de caracteres terminada por una nueva línea, dependiendo de la entrada proporcionada por el usuario. Deshazte de esos extraños personajes antes de leer una cadena del stdin.

 char ch; while((ch = getchar()) != '\n' || ch != EOF); // null statement fgets(in, 100, stdin); 

La llamada de fgets anterior significa que leerá a lo sumo 100-1 = 99 (guarda un espacio de caracteres para el byte nulo de terminación que se agrega al búfer que se lee antes de salir) los caracteres de la secuencia señalada por stdin y los almacena En el búfer apuntado por in . fgets cerrará si encuentra EOF , '\n' o ya ha leído 100-1 caracteres, cualquiera de las tres condiciones ocurre primero. Si lee una nueva línea, la almacenará en el búfer.

Si el usuario ingresa 100 caracteres o más en este caso, entonces los caracteres extraños se encontrarán en el búfer de entrada, lo que puede interferir con la subsiguiente operación de entrada de caracteres o cadenas mediante las llamadas scanf , fgets , getchar , etc. Puede comprobar esto comprobando la longitud de la cadena in .

 if(strlen(in) > 99) { // extraneous chars lying around in the input buffer // read and discard them char ch; while((ch = getchar()) != '\n' || ch != EOF); // null statement }