Function scanf not executed

Solved
arscy Posted messages 196 Status Member -  
mamiemando Posted messages 33228 Registration date   Status Moderator Last intervention   -

Hello,

This is not the first time I have encountered this type of issue, and I still haven't identified the why or how. If you can shed some light on this, it could help resolve my future troubles regarding this matter.

My C program compiles without errors with the flags -W -Wall -Werror. It runs fine until it reaches a scanf that isn't even considered. As a result, my program crashes as soon as the variable is accessed.

When I look at this scanf, I have indeed declared the variable (uninitialized) beforehand with the correct type.
Below is an excerpt of the relevant code sequence:

... char propose; while (condition1 == 0 && condition2 == 0){ fonction1(var1); // void type function that performs an output printf("Please propose a letter:\n"); scanf("%c", &propose); printf("It works\n"); //visible at execution without having had the opportunity to enter any data fonction2(propose, ...); //crash: Segmentation fault (core dumped) ... }

The execution proceeds normally until the first printf of the copied sequence. However, I directly see the second printf. I encountered this same issue earlier in the day with another non-executed scanf and I honestly don’t even know how I resolved that.

Thank you in advance for your time.

4 answers

  1. [Dal] Posted messages 6122 Registration date   Status Contributor Last intervention   1 108
     

    To reproduce what you observed, try the following code:

    #include <stdio.h> int main(void) { printf("Please enter an integer\n"); int n; scanf("%d", &n); printf("I retrieved: [%d]\n", n); printf("Please enter a character and press enter\n"); char c; scanf("%c", &c); printf("I retrieved: [%c]\n", c); return 0; } 

    If we run this code by entering 12 and pressing enter, the 2nd scanf() doesn't interrupt the program (the execution seems to "skip" the next scanf()) and this produces the following:

     $ gcc -Wall -Wextra 37704224.c $ ./a.out Please enter an integer 12 I retrieved: [12] Please enter a character and press enter I retrieved: [ ] 

    The following occurred:

    • we typed 12 and Enter
    • scanf() only retrieved 12 and left Enter in the stdin buffer
    • actually the next scanf() is not skipped, it just consumes the next available char in the stdin buffer
    • the Enter character was thus read by the next scanf(), as we can see in the output of the retrieved char that we attempt to display in brackets

    scanf() always leaves at least the newline character '\n' in the stdin buffer.

    It may leave more than that (for example, if we had entered "12toto" and pressed Enter, the first scanf() would have only retrieved 12 and would have left "toto" and Enter in stdin which could be consumed by the next scanf() calls).

    To ensure that the stdin buffer is empty, after each scanf() we can do this:

     int c; while((c = getchar()) != '\n' && c != EOF) /* consume everything left in stdin */ ;

    We can put these lines in a function empty_stdin() and call it after each scanf() call.

    2
    1. arscy Posted messages 196 Status Member 8
       

      -_-
      So in a perfect world in C, we should ensure that buffers are always empty in order not to end up with a mess like the one I encounter regularly?

      0
      1. [Dal] Posted messages 6122 Registration date   Status Contributor Last intervention   1 108 > arscy Posted messages 196 Status Member
         

        stdin is a buffered stream.

        If your code only uses part of its content but expects it to be empty, it's obvious that you're heading towards unexpected behaviors.

        Your use of scanf() does not empty the buffer.

        fgets() on stdin only empties it if the line read, including the '\n', fits within the size specified in the second argument. If the size is insufficient, the buffer will retain the unconsumed characters.

        For the stdin buffer to be empty, your code must consume what is in it. That's all.

        When you use these functions, you need to understand how they work, to determine whether the buffer needs to be emptied or not based on what you've done and what you want to do next.

        0
      2. [Dal] Posted messages 6122 Registration date   Status Contributor Last intervention   1 108 > [Dal] Posted messages 6122 Registration date   Status Contributor Last intervention  
         

        Here is an example of a program where we use the behavior of scanf() on the stdin buffer to read in a loop what it contains as long as there are integers to process.

        #include <stdio.h> int main(void) { printf("Please enter integer numbers separated by spaces, " "terminate with a . (or another non-numeric character) and " "press Enter, to display the sum of these numbers\n"); int sum = 0; int n; while (scanf("%d", &n) == 1) { sum = sum + n; printf("sum = %d\n", sum); } printf("The sum of these different numbers is: %d\n", sum); return 0; } 

        This produces, at runtime:

         $ gcc -Wall -Wextra 37704224.c $ ./a.out Please enter integer numbers separated by spaces, terminate with a . (or another non-numeric character) and press Enter, to display the sum of these numbers 10 20 30 40. sum = 10 sum = 30 sum = 60 sum = 100 The sum of these different numbers is: 100 

        Our program consumes the integers present in the stdin buffer and does not clear the buffer between each call to scanf() because it is designed to process what is there through successive calls.

        When scanf() returns anything other than 1, it means that something other than an integer separated by "white" characters has been encountered, and we exit the loop.

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

        1
  2. NHenry Posted messages 15235 Registration date   Status Moderator Last intervention   387
     

    It may happen that the terminal buffers the keys, so you might think you haven't pressed a key, but you actually have.

    I suggest you change your

    printf("ça marche\n");

    to

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

    This way it will display the key pressed along with its code.

    If you see that the code is 0, it's a null character that has been retrieved; otherwise, check the result.

    Regarding the CoreDump, I suspect another cause.

    But I don't usually work with C/C++, so I wouldn't know how to go further without more details (and even then, I cannot guarantee).


    I mainly work in VB6, VB.NET, and C#, but moderation often leads me to other languages.
    In VB.NET, remember to enable "Option Explicit" and "Option Strict".

    1
  3. mamiemando Posted messages 33228 Registration date   Status Moderator Last intervention   7 940
     

    Hello,

    If we talked about a stream in which we write, we would want to use the fflush function. Unfortunately, as explained in this article, we cannot use it for stdin.

    However, as suggested by [Dal] (#5), we can consume the residual characters by writing a function to handle it. This is what is proposed here. If I take the code proposed by [Dal] (#3), we end up with this:

    #include <stdio.h> void clear_stdin() { for (int c = 0; c != '\n' && c != EOF; c = getchar()); } int main() { int n; printf("Please enter an integer and press enter\n"); scanf("%d", &n); clear_stdin(); printf("I retrieved: [%d]\n", n); char c; printf("Please enter a character and press enter\n"); scanf("%c", &c); clear_stdin(); printf("I retrieved: [%c]\n", c); return 0; }

    Which gives the following output upon execution:

     Please enter an integer and press enter 123 I retrieved: [123] Please enter a character and press enter a I retrieved: [a]
    1
  4. arscy Posted messages 196 Status Member 8
     

    Resolution of the variable reading issue:
    the variable named 'propose' I renamed to a shorter name: 'try'.
    That solved my scanf problem.
    I don't understand.

    0