Función scanf no ejecutada
ResueltoNo es la primera vez que me encuentro con este tipo de problema, y aún no he identificado el porqué del cómo. Si pueden aclarármelo, podría resolver bien mis tribulaciones futuras a este respecto.
Mi programa en C compila sin errores con las banderas -W -Wall -Werror. Se ejecuta y responde bien hasta alcanzar un scanf que ni siquiera se toma en cuenta. En consecuencia, mi programa se bloquea en cuanto se solicita la variable.
Cuando me pongo a revisar este scanf, he declarado la variable (no inicializada) en antecendentes con el tipo correcto.
A continuación un extracto de la secuencia de código implicada:
... char propose; while (condition1 == 0 && condition2 == 0){ fonction1(var1); // función de tipo void que realiza una salida printf("Proposez une lettre:\n"); scanf("%c", &propose); printf("ça marche\n"); //visible en ejecución sin haber tenido la posibilidad de introducir ningún dato fonction2(propose, ...); //crash: Erreur de segmentation (core dumped) ... }
El desarrollo es normal hasta el primer printf de la secuencia copiada. Por otro lado, veo directamente el segundo printf. He encontrado este mismo problema un poco antes en el día en el mismo tipo de problema con un scanf no ejecutado y admito que no supe ni cómo lo resolví.
Gracias de antemano por su tiempo.
4 respuestas
-
Para reproducir lo que has observado, prueba el siguiente código:
#include <stdio.h> int main(void) { printf("Veuillez taper un entier\n"); int n; scanf("%d", &n); printf("J'ai récupéré : [%d]\n", n); printf("Veuillez taper un caractère et taper entrée\n"); char c; scanf("%c", &c); printf("J'ai récupéré : [%c]\n", c); return 0; }Si ejecutas este código introduciendo 12 y Enter, el segundo scanf() no interrumpe el programa (la ejecución parece "saltar" el scan() siguiente) y esto produce lo siguiente:
$ gcc -Wall -Wextra 37704224.c $ ./a.out Veuillez taper un entier 12 J'ai récupéré : [12] Veuillez taper un caractère et taper entrée J'ai récupéré : [ ]Lo que ocurrió fue lo siguiente:
- introdujiste 12 y Enter
- scanf() solo recuperó 12 y dejó el Enter en el buffer de stdin
- de hecho el siguiente scanf() no se salta, simplemente toma el próximo carácter disponible en el buffer de stdin
- el carácter de Enter fue leído por el siguiente scanf(), como se puede ver en la impresión del carácter recuperado que intentamos mostrar entre corchetes
scanf() siempre deja, al menos, el carácter de retorno de línea '\n' en el buffer de stdin.
Puede dejar más (por ejemplo, si hubieras escrito "12toto" y Enter, el primer scanf() habría recuperado solo 12 y habría dejado "toto" y Enter en stdin, que podrían ser consumidos por las siguientes llamadas a scanf().
Para asegurar que el buffer de stdin está vacío, después de cada scanf() podemos hacer lo siguiente:
int c; while((c = getchar()) != '\n' && c != EOF) /* consumimos todo lo que quede en stdin */ ;
Podemos poner estas líneas tal cual en una función empty_stdin() y llamarla después de cada llamada a scanf().
-
-_-
Entonces, en un mundo perfecto en C habría que asegurarse de que los buffers estén siempre vacíos para no terminar con un descontrol como el que encuentro regularmente?stdin es un flujo con buffer.
Si tu código solo usa una parte de su contenido, pero espera que esté vacío, es evidente que vas a enfrentarte a comportamientos inesperados.
Tu uso de scanf() no vacía el buffer.
fgets() sobre stdin lo vacía solo si la línea leída, incluido el '\n', cabe en el tamaño especificado en el segundo argumento. Si el tamaño es insuficiente, el buffer conservará los caracteres no consumidos.
Para que el buffer de stdin esté vacío, tu código debe consumir todo lo que se encuentra allí. Eso es todo.
Cuando uses estas funciones, debes saber cómo funcionan, para poder determinar si el buffer debe vaciarse o no según lo que hayas hecho y lo que quieras hacer después.
Aquí tienes un ejemplo de programa donde se usa el comportamiento de scanf() sobre el buffer stdin para leer en bucle lo que hay allí mientras haya enteros por procesar.
#include <stdio.h> int main(void) { printf("Veuillez taper des nombres entiers séparés par des espaces, " "terminer par un . (ou un autre caractère non numérique) et " "taper Entrée, pour afficher l'addition de ces nombres\n"); int addition = 0; int n; while (scanf("%d", &n) == 1) { addition = addition + n; printf("addition = %d\n", addition); } printf("L'addition de ces différents nombres donne : %d\n", addition); return 0; }cela donne, à l'exécution :
$ gcc -Wall -Wextra 37704224.c $ ./a.out Veuillez taper des nombres entiers séparés par des espaces, terminer par un . (ou un autre caractère non numérique) et taper Entrée, pour afficher l'addition de ces nombres 10 20 30 40. addition = 10 addition = 30 addition = 60 addition = 100 L'addition de ces différents nombres donne : 100Nuestro programa consume los enteros presentes en el buffer de stdin y no vacía el buffer entre cada llamada a scanf() porque está diseñado para procesar lo que allí se encuentra mediante llamadas sucesivas.
Cuando scanf() devuelve algo distinto de 1, es que se ha encontrado algo distinto de un entero separado por caracteres "blancos", y terminamos el bucle.
-
Puede ocurrir que la terminal almacene en búfer las teclas y por lo tanto no pienses haber pulsado una tecla, pero que en realidad sí lo hayas hecho. Te propongo cambiar tu
printf("ça marche\n");porprintf("ça marche code %d caractere %c c'est bon\n", propose, propose);De esta forma te mostrará la tecla vista con su código. Si ves que code vale 0, es un nulo el que se está capturando; de lo contrario, mira el resultado. Con respecto al CoreDump, sospecho más bien de otra causa. Pero no hago C/C++ habitualmente, así que no sabría ir más allá sin más detalles (y aun así, no lo garantizo).
Intervengo principalmente en VB6, VB.NET y C#, pero la moderación me suele llevar a otros lenguajes.
En VB.NET piensa en activar "Option Explicit" y "Option Strict". -
Hola,
Si hablamos de un flujo en el que escribimos, tendríamos ganas de usar la función fflush. Desafortunadamente, como explica este artículo, no se puede usar para stdin.
Sin embargo, como sugiere [Dal] (#5), se pueden consumir los caracteres residuales escribiendo una función que se encargue de ello. Eso es lo que se propone aquí. Si tomo el código propuesto por [Dal] (#3) llegamos a lo siguiente :
#include <stdio.h> void clear_stdin() { for (int c = 0; c != '\n' && c != EOF; c = getchar()); } int main() { int n; printf("Veuillez taper un entier puis entrée\n"); scanf("%d", &n); clear_stdin(); printf("J'ai récupéré : [%d]\n", n); char c; printf("Veuillez taper un caractère puis entrée\n"); scanf("%c", &c); clear_stdin(); printf("J'ai récupéré : [%c]\n", c); return 0; }Lo que da en la ejecución:
Veuillez taper un entier puis entrée 123 J'ai récupéré : [123] Veuillez taper un caractère puis entrée a J'ai récupéré : [a] -
Resolución del problema de lectura de la variable:
la variable que se llamaba 'propose' la he renombrado a un nombre más corto: 'try'.
Eso ha resuelto mi problema con scanf.
No lo entiendo.