Fonction scanf pas exécutée

Résolu/Fermé
arscy Messages postés 173 Date d'inscription dimanche 26 janvier 2014 Statut Membre Dernière intervention 5 octobre 2023 - Modifié le 20 oct. 2022 à 11:02
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 - 20 oct. 2022 à 11:15

Bonjour,

Ce n'est pas la première fois que je rencontre ce type de souci, et je n'ai toujours pas identifié le pourquoi du comment. Si vous arrivez à m'éclairer ça pourrait bien résoudre mes tribulations futures à ce sujet.

Mon programme en C compile sans erreur avec les flags -W -Wall -Werror. Il se lance, et répond bien jusqu'à atteindre un scanf qui n'est même pas considéré. Par conséquent mon programme crashe dès lors que la variable est sollicitée.

Lorsque je me penche sur ce scanf, j'ai bien déclaré la variable (non initialisée) en amont avec le bon type.
Ci-dessous un extrait de la séquence de code concernée :
 

...
char propose;

while (condition1 == 0 && condition2 == 0){
      fonction1(var1); // fonction de type void qui effectue un affichage
      printf("Proposez une lettre:\n");
      scanf("%c", &propose);
      printf("ça marche\n"); //visible à l'exécution sans avoir eu la possibilité d'entrer la moindre donnée
      fonction2(propose, ...); //crash: Erreur de segmentation (core dumped)
      ...
}

Le déroulement est normal jusqu'au premier printf de la séquence recopiée. Par contre, je vois directement le second printf. J'ai rencontré ce même souci un peu plus tôt dans la journée sur le même type de problème avec un scanf non exécuté et j'avoue que je n'ai même pas su comment j'ai résolu ça.

Merci d'avance pour votre temps.

4 réponses

[Dal] Messages postés 6198 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 13 décembre 2024 1 096
Modifié le 9 oct. 2022 à 22:27

Pour reproduire ce que tu as constaté, essaye le code suivant :

#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 on exécute ce code en tapant 12 et entrée, le 2ème scanf() n'interrompt pas le programme (l'exécution semble "sauter" le scan() suivant) et cela produit ceci :

$ 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é : [
]

Il s'est passé la chose suivante :

  • on a tapé 12 et Entrée
  • scanf() a seulement récupéré 12 et a laissé Entrée dans le tampon de stdin
  • en fait le scanf() suivant n'est pas sauté, il consomme juste le prochain char disponible dans le tampon de stdin
  • le caractère entrée a donc été lu par le prochain scanf(), comme on peut le constater dans l'affichage du char récupéré que l'on tente d'afficher entre crochets

scanf() laisse toujours, au moins, le caractère de retour à la ligne '\n' dans le tampon de stdin.

Il peut laisser plus que cela (par exemple, si on avait tapé "12toto" et Entrée, le premier scanf() n'aurait récupéré que 12 et aurait laissé "toto" et Entrée dans stdin qui pourront être consommés par le(s) prochain(s) appel(s) à scanf().

Pour être sûr que le tampon de stdin est vide, après chaque scanf() on peut faire ceci :

	int c;
	while((c = getchar()) != '\n' && c != EOF)
		/* on consomme tout ce qui reste dans stdin */ ;

On peut mettre ces lignes telles quelles dans une fonction empty_stdin() et l'appeler après chaque appel à scanf().

2
arscy Messages postés 173 Date d'inscription dimanche 26 janvier 2014 Statut Membre Dernière intervention 5 octobre 2023 9
9 oct. 2022 à 23:01

-_-
Donc dans un monde parfait en C il faudrait s'assurer que les tampons sont systématiquement vides histoire de ne pas se retrouver avec une cabriole du genre de ce que je rencontre régulièrement?

0
[Dal] Messages postés 6198 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 13 décembre 2024 1 096 > arscy Messages postés 173 Date d'inscription dimanche 26 janvier 2014 Statut Membre Dernière intervention 5 octobre 2023
Modifié le 9 oct. 2022 à 23:46

stdin est un flux bufferisé.

Si ton code n'utilise qu'une partie de son contenu, mais s'attend à ce qu'il soit vide, c'est évident que tu vas au devant de comportements inattendus.

Ton utilisation de scanf() ne vide pas le tampon.

fgets() sur stdin ne le vide que si la ligne lue, y compris le '\n', tient dans la taille spécifiée dans le 2ème argument. Si la taille est insuffisante, le tampon conservera les char non consommés.

Pour que le tampon de stdin soit vide, il faut que ton code consomme ce qui s'y trouve. C'est tout.

Lorsque tu utilises ces fonctions, tu dois savoir comment elles fonctionnent, pour savoir déterminer si le tampon doit être vidé ou pas selon ce que tu as fait et ce que tu veux faire par la suite.

0
[Dal] Messages postés 6198 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 13 décembre 2024 1 096 > [Dal] Messages postés 6198 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 13 décembre 2024
Modifié le 10 oct. 2022 à 00:12

Voilà un exemple de programme où on utilise le comportement de scanf() sur le buffer stdin pour lire en boucle ce qui s'y trouve tant qu'il y a des entiers à traiter.

#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 : 100

Notre programme consomme les entiers présents dans le tampon de stdin et ne vide pas le tampon entre chaque appel à scanf() parce qu'il est conçu pour traiter ce qui s'y trouve par appels successifs.

Quand scanf() retourne autre chose que 1, c'est qu'autre chose qu'un entier séparé par des caractères "blancs" a été rencontré, et on termine la boucle.

https://cplusplus.com/reference/cstdio/scanf/#return

1
NHenry Messages postés 15172 Date d'inscription vendredi 14 mars 2003 Statut Modérateur Dernière intervention 11 décembre 2024 350
Modifié le 10 oct. 2022 à 00:17

II peut arriver que le terminal buffer les touches et donc que tu ne penses pas avoir appuyé sur une touche, mais que si en fait.

Je te propose de changer ton

printf("ça marche\n");

en

printf("ça marche code %d caractere %c c'est bon\n", propose, propose);

Comme ça ça va t'afficher la touche vue avec son code.

Si tu vois que code à 0 c'est un nul qui est récupéré, sinon, regarde le résultat.

Concernant le CoreDump, je suspecte plutôt une autre cause.

Mais je ne fais pas de C/C++ habituellement, donc je ne saurais aller plus loin sans plus de détail (et même là, je ne garanti pas).


1
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 7 811
Modifié le 20 oct. 2022 à 11:18

Bonjour,

Si on parlait d'un flux dans lequel on écrit, on aurait envie d'utiliser la fonction fflush. Malheureusement, comme l'explique cet article, on ne peut pas l'utiliser pour stdin.

On peut toutefois, comme le suggère [Dal] (#5), consommer les caractères résiduels en écrivant une fonction qui va s'en charger. C'est ce qui est proposé ici. Si je reprends le code proposé par [Dal] (#3) on aboutit à ceci :

#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;
}

Ce qui donne à l'exécution :

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]
1
arscy Messages postés 173 Date d'inscription dimanche 26 janvier 2014 Statut Membre Dernière intervention 5 octobre 2023 9
9 oct. 2022 à 18:43

Résolution du souci de lecture de la variable :
la variable s'appelant 'propose' je l'ai renommée en un nom plus court : 'try'.
Ça a résolu mon affaire de scanf.
Je ne comprends pas.

0