0
#include <stdio.h>


struct mychar {
    char value;
    struct mychar *nextPtr;
};

typedef struct mychar Mychar;


void instructions();
void append(Mychar **, char );
void printlist(Mychar *);


int main(){
    instructions();

    Mychar *startPtr = NULL;

    unsigned int choice;
    char newchar;
    do {
        scanf("%d",&choice);
        switch (choice) {
            case 1:
                printf("\nWrite the character you want to add.");
                printf("\n> ");
                scanf(" %c", &newchar);
                append(&startPtr, newchar);
                printlist(startPtr);
                break;
            case 2:
                break;
            default:
                printf("\nError, try again.\n");
                //main();
                instructions();
                break;
        }
    } while (choice!=3);
    printf("\n\nEnd of run.\n");
}


void instructions(){
    printf("\nSelect operation. 1 to add, 2 to remove, 3 to exit.");
    printf("\n> ");
}


void append(Mychar **sPtr, char newvalue){
    Mychar *newlinkPtr = calloc (1, sizeof(Mychar));
    newlinkPtr->value = newvalue;
    newlinkPtr->nextPtr = NULL;

    Mychar *previousPtr = NULL;
    Mychar *currentPtr = *sPtr;

    while(currentPtr!=NULL && newvalue > currentPtr->value){
        previousPtr = currentPtr;
        currentPtr = currentPtr->nextPtr;
    }

    if (previousPtr){
        previousPtr->nextPtr = newlinkPtr;
        newlinkPtr->nextPtr = currentPtr;
    } else {
        *sPtr = newlinkPtr;
    }

}


void printlist(Mychar *currentPtr){
    printf("\n\nCurrent list:\n");
    while (currentPtr!=NULL){
        printf("%c", currentPtr->value);
        currentPtr = currentPtr->nextPtr;
    }
}

Why do I have this behaviour? If I run the program, after I enter 1, it prints the "current list" and leave the scanf input opened, so I can enter the value only after "current list" printed. Also, "current list" should be called only after I enter the character with scanf, since the function printlist is AFTER the scanf... but actually this is what happens:

Select operation. 1 to add, 2 to remove, 3 to exit.
> 1

Write the character you want to add.
> a


Current list:
ab

Write the character you want to add.
> 

Current list:
abc

Write the character you want to add.
> 

Current list:
abcd

Write the character you want to add.
> 

Current list:
abcd
Mnkisd
  • 504
  • 2
  • 12
  • @user3121023 I think you didn't read the thread :( – Mnkisd May 28 '20 at 19:26
  • Something in `append()` is probably causing undefined behavior and corrupting the heap. – Barmar May 28 '20 at 19:36
  • I suggest you step through `append()` in the debugger and make sure it's working as intended. – Barmar May 28 '20 at 19:37
  • I think `while(*sPtr!=NULL && newvalue > currentPtr->value){` should be `while(currentPtr!=NULL && newvalue > currentPtr->value){` – Barmar May 28 '20 at 19:38
  • This must be one of the most FAQ here, probably several times a day. Please change `scanf("%c", &newchar);` to `scanf(" %c", &newchar);` with an added space. Please see [scanf() leaves the newline char in the buffer](https://stackoverflow.com/questions/5240789/scanf-leaves-the-new-line-char-in-the-buffer). Some explanation: most of the format specifiers for `scanf` automatically filter leading whitespace, but `%c` and `%[...]` and `%n` do not. Adding a space in front of the `%` instructs `scanf` to filter leading whitespace here too. – Weather Vane May 28 '20 at 19:41
  • @WeatherVane I think you didn't read all the thread. – Mnkisd May 28 '20 at 19:43
  • duplicate of https://stackoverflow.com/questions/62071579/scanf-makes-do-while-loop-stuck – mlp May 28 '20 at 19:43
  • You've got more than one bug. The first bug is in the `scanf("%c", ...)`. You need a space, as you already know. To test that the `scanf` is working correctly, remove the calls to `append` and `printlist`, and instead `printf("The character is '%c'\n", newchar)` Once you've got that working, you can move on to the next bug. – user3386109 May 28 '20 at 19:44
  • @Barmar yeah, replacing it now I finally gets the printed list, but there still are the same problems of the thread. – Mnkisd May 28 '20 at 19:44
  • @mlp you didn't read all the thread. – Mnkisd May 28 '20 at 19:45
  • @Mnkisd the question title "Missed scanf and function goes on without it" and in the narrative "it doesn't let me enter any character... It only prints the current list" is a typical result. If the program carries on and faults, that's another issue. – Weather Vane May 28 '20 at 19:46
  • 2
    Your real problem is with the segmentation fault. Why don't you fix the scanf in the posted code, and fix the title, so people will stop trying to close it as a duplicate of the `space-%c` answer. – Barmar May 28 '20 at 19:49
  • @Barmar there isn't a seg fault anymore, I fixed it. I also updated the thread. – Mnkisd May 28 '20 at 19:52
  • 2
    @Mnkisd You updated the code but it is still missing the space before %c, to do what you want that is necessary, not an option. – isrnick May 28 '20 at 19:54
  • @isrnick ok, done it. – Mnkisd May 28 '20 at 19:56
  • So has it, or hasn't it cured the segfault? If it has, was that a red herring from an undefined input? – Weather Vane May 28 '20 at 19:57
  • @WeatherVane it did. but not the printf/scanf problem – Mnkisd May 28 '20 at 19:58
  • @Mnkisd add a `printf("\n");` at the end of the `printlist` funcion and a call to `instructions()` before the scanf or at the end of do-while loop, the problem is that you are not being prompted to type a new number. – isrnick May 28 '20 at 20:09
  • @isrnick this fixed the problem... but even if now works I ask to myself... why it continued to print `Write the character you want to add.` like if the switch loop was like "stuck" in case 1 ? – Mnkisd May 28 '20 at 20:26
  • ...because [`scanf()` leaves the newline char in the buffer](https://stackoverflow.com/questions/5240789/scanf-leaves-the-new-line-char-in-the-buffer), as mentioned, which was taken as its input, and anything else you typed also remained in the input buffer, and if it was a character that can't be processed by `%d` then it sticks there. – Weather Vane May 28 '20 at 20:32
  • @Mnkisd because as you were not asked to type a number you were actually typing a letter in the scanf with %d , so the scanf failed and the program continued without changing the value of the variable `choice`. – isrnick May 28 '20 at 20:33
  • lol, cool, thanks! – Mnkisd May 28 '20 at 20:34

1 Answers1

1

The lesson to take form this is to always check scanf for 0 return, at the very least, EOF check is also advised, and act accordingly, as for the order of events of your code, it's not quite there, with some tweaks you can have a nice, bad input proof, I/O sequence:

void clear_stdin() { //stdin buffer clearing function 
    int c;
    while ((c = getchar()) != '\n' && c != EOF){}
}
do {
    instructions(); //move inside the loop, user will be prompted in each cycle
    while (scanf("%d", &choice) == 0) {
        printf("\nError, try again.\n");
        instructions();
        clear_stdin(); // if input fails clear the buffer
    }
    clear_stdin(); // clear the buffer for 1hjh type input
    switch (choice) {
    case 1:

        printf("\nWrite the character you want to add.");
        printf("\n> ");
        while (scanf(" %c", &newchar) == 0) { //this can be a pattern
            clear_stdin();                    //see @ismick comment
        }                                     //
        clear_stdin();                        //
        append(&startPtr, newchar);
        printlist(startPtr);
        break;
    case 2:
        break;
    case 3:
        printf("\n\nEnd of run.\n"); //if you dont have a case default will catch 3
        break;
    default:
        printf("\nError, try again.\n");
        break;
    }
} while (choice != 3);
anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • There is still a chance that the user could type bad input in the first `scanf`... Something like `1abc` typed in the first `scanf` would be accepted but would end up skipping the second `scanf`. – isrnick May 28 '20 at 21:58
  • @isrnick, yes well spotted, will add the first caught character to the list, a clearence at the beginning of the case should fix it. – anastaciu May 28 '20 at 22:09
  • At this point a `clear()` function with the clearence routine would be appropriate – anastaciu May 28 '20 at 22:11
  • 1
    Yes, I would call it `clearstdin()` for clarity. Also instead of `if(scanf...` you could use `while(scanf...` so it doesn't continue to the next steps while a valid input isn't typed. – isrnick May 28 '20 at 22:17
  • @isrnick, the while is also a good adition, I guess we're 99% bullet proof now. ;) – anastaciu May 28 '20 at 22:26
  • 1
    I noticed you placed the `clear_stdin()` at the beginning of `case 1:`, and at the end of the `do-while` loop. Why not place it after each `while (scanf) {clear_stdin}` instead? So that it becomes a pattern `while (scanf) {clear_stdin} clear_stdin` that can be re-used whenever you use the `scanf` function. – isrnick May 28 '20 at 22:29
  • 1
    @isrnick, yes that can be done, it's a good solution, I wsn't going for perfection but now we're near. I would make the routines but maybe that's too much – anastaciu May 28 '20 at 22:40
  • 1
    @anastaciu thanks for answer. 2 questions: 1) why do you put `clear_stdin()` even after the `while(scanf)` part? Why is it needed? 2) what's the difference between using your `clear_stdin()` and using `fflush(stdin)` ? – Mnkisd May 29 '20 at 09:32
  • 1
    @Mnkisd `fflush(stdin)` is undefined behaviour, the `fflush` function is only meant to be used for output streams, but `stdin` is an input stream, so despite it working to clear `stdin` in some system or compiler it is also as likely to fail or do something totally unexpected in another. – isrnick May 29 '20 at 11:25
  • 1
    @Mnkisd, 1) It's basically input checking, consider this, if you type something like `1hhjk` in option selection, 1 will be parsed succefully, scanf will return 1 and `hhjk` will remain on the buffer being caught by the next scanfs. The same happens with the characters input, if you type `asdfsd`, only `a` will be parsed, so we need to clear the rest. As for 2) [ismick just answered it](https://stackoverflow.com/questions/62072901/why-does-my-append-function-cause-a-segfault/62074708?noredirect=1#comment109805931_62074708). More details [here](https://stackoverflow.com/a/34247021/6865932) – anastaciu May 29 '20 at 11:35
  • 1
    @Mnkisd the `clear_stdin()` is there after the `while(scanf)` loop to clear anything left in the `stdin` after the `scanf` read succeeds, remember that `scanf` leaves the new line character in the `stdin`, and also that the user may type bad input that is successfully read by `scanf`, for example a `scanf` with `%d` would have no problem reading 1 when the user types `1abc`, so in this case it would be necessary to clear the `abc` plus new line characters left in the `stdin`. – isrnick May 29 '20 at 11:46
  • @anastaciu what could I do to clear stdin from strings or ints instead of char? Can I use your clear_stdin() function as well? Also, why did you put != '\n' ? – Mnkisd May 30 '20 at 13:29
  • @Mnkisd, yes, the function clears whatever is in the buffer. – anastaciu May 30 '20 at 17:16
  • @anastaciu why did you put != '\n' ? – Mnkisd May 30 '20 at 17:26
  • @Mnkisd, when you press enter a `\n` character is added to the buffer, so you know when it is consumed by `getchar()` there are no more characters in there. Reading cycle literaly: while the consumed character is different from `\n` read the next one. – anastaciu May 30 '20 at 17:42