0

I am trying to write a correct console application for linked list usage, so i need to scan a number of a command in a infinite loop and do something due to switch case option. So i am using scanf for this but the problem is when the next line doesnt contain number it loops and starts printing not even default value.

`while(1)
    {
            printf("Enter a number of a command.\n");
            scanf("%d",&command);
            switch(command)
            {
            case -1:
            ....
            default:
                  printf("Reenter command.\n");
                  break;
            }
    }

It seems like when i am reading the infinite amount of data stack gets rewrited. I know i have to limit the amount of symbols reading, but dont understand how to do this in right way.

Using gcc version 5.4.0 (GCC), c99 on Ubuntu 18.04.2 LTS

Dmitry
  • 3
  • 1
  • 7
    I'd start by checking the result of the scanf before even considering what it actually (supposedly) *scanned* rather than blindly assuming it just "worked". It has a documented return value for a reason. Perhaps use it *first*, and when things go south, discard input stream data through newline or EOF (whichever is first). – WhozCraig Nov 29 '20 at 22:36
  • 3
    You either want a `scanf` pattern that gets the number but also guarantees that it slurps up the newline, or, do `fgets` [which forces entire line] and then use `sscanf` on the buffer. Then, if it isn't a number, it's easy to reloop [without having deal with funny lines like: `not_a_number 23`]. You want the reloop to toss anything [bad] on the first line and force a completely new line to be read in. IMO, `fgets` and then `strtol` is a better choice. – Craig Estey Nov 29 '20 at 22:53
  • 3
    When `scanf` sees input that it cannot match to the format it is presently using, *that input is left unread*. Thus, in your example, if `scanf` cannot match the input to your format then looping back to try again with the same format is very unlikely to yield a different result. – John Bollinger Nov 29 '20 at 22:59
  • 3
    You've just found out why using `scanf()` to read input is a bad idea. It's much more reliable to read input line-by-line with something like `fgets()` or `getline()` and then parse the input data yourself. At least that way unexpected input doesn't leave your input stream in an unknown state like `scanf()` does... – Andrew Henle Nov 29 '20 at 23:19
  • 1
    You may want to read this: [A beginners' guide away from scanf()](http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html) – Andreas Wenzel Nov 30 '20 at 00:03
  • It may be easier to help you with your question if you provided a [mre] and stated the exact input, expected output and actual output. – Andreas Wenzel Nov 30 '20 at 00:47

1 Answers1

1

I don't have enough reputation to comment, but this might be what you are looking for. Also try to be more descriptive. I have pasted your code and the only problem I could find is that when you press enter without inserting a number(i.e. a letter) it skips. This should fix it:

int readInt(const char *message, int min, int max){
    int num, control;
    do{
        printf("%s (%d a %d) :", message, min, max);
        control = scanf ("%d", &num);
        cleanBufferStdin();  
        if (control == 0)
        {
            printf("You should enter a number \n");
        }
        else{
            if(num<min || num>max)
            {
                printf("Number is invalid.\n");
            }
        }
    }  
    while(num<min || num>max || control ==0);

    return num;
}
void cleanBufferStdin(void)
{
    char chr;
    do
    {
        chr = getchar();
    }
    while (chr != '\n' && chr != EOF);
}

I coded for a bit more, and in another interpretation of your question(this one not only detects if you just pressed enter but if you didnt place an integer i didnt check if negative numbers work) I used this function:

//DONT FORGET TO #DEFINE WRONG_REQUEST_MACRO "SOME MESSAGE"
void readString(const char message*, char arrayChars *, int maxChars){
    int stringSize;
    unsigned char flag=0;
    do{
        flag =0;
        printf("%s", message);
        fgets(arrayChars, maxChars, stdin);
        
        
        stringSize = strlen(arrayChars);
        if (stringSize == 1){
            printf("[INFO]Empty request. You just pressed ENTER.\n"); 
            flag=1;
        }
        if (atoi(arrayChars)==0&&arrayChars[0]!=0){
            printf("[INFO]You didn't enter a number.\n");
            flag=1;

        }
    } while (flag == 1);

    if (arrayChars[stringSize - 1] != '\n'){
        clearBuffer(); 
    }else{
        arrayChars[stringSize - 1] = '\0'; 
    }

    while (strchr(arrayChars, '\'') != NULL || strchr(arrayChars, '?') != NULL || strchr(arrayChars, '*') != NULL || strchr(arrayChars, '\"') != NULL){
        printf("%s ' %s '", WRONG_REQUEST_MACRO, arrayChars);
        break;
    }
}

this should be used like

int command;
char message[20];//yes this could be used with a char pointer but lets assume op doesnt know how to allocate memory or work with char pointers it wouldn't change that much but if he does know how to do it he will promptly change

readString("something something i suppose\n",message,20); 
command=atoi(message);

Welp the last although its filled with debugging "duplicates" should work

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
Imeguras
  • 436
  • 6
  • 18
  • 1
    `int readInt(char message[MAX_STRING], int min, int max){` -> `int readInt(const char const *message, int min, int max){` may be better – Ed Heal Nov 30 '20 at 00:03
  • 1
    In the line `if (atoi(arrayChars)==0&&arrayChars[0]!=0){`, you may want to change it to an `else if`. Or even better: You may want to change the two `flag = 1` lines to `continue` statements and change the `do`...`while` loop to an infinite loop (`for(;;)` or `while(1)`). However, in that case, you must add a `break` statement at the end of the loop. That way, you will no longer need the variable `flag`. Using a `goto` label instead of a loop may be an even cleaner solution in this case, but [some people say that goto should not be used if possible](https://stackoverflow.com/q/24451/12149471). – Andreas Wenzel Nov 30 '20 at 01:18
  • In the line `if (atoi(arrayChars)==0&&arrayChars[0]!=0){`, is the expression `arrayChars[0]!=0` intended? Or did you rather mean `'0'` instead of `0`, i.e. the character code? In other words, do you intend to test whether the first character is the digit `'0'` or are you checking for the null terminating character? – Andreas Wenzel Nov 30 '20 at 01:24
  • 1
    `control = scanf ("%d", &num);` --> leads to `control` with a value of 0, 1 or `EOF`. This code does not handle the `EOF` case. – chux - Reinstate Monica Nov 30 '20 at 01:45
  • im tired its late, tommorow ill check in detail your comments also yes the code was written really fast because i was worried that the huge amount of comments would come in But they still did – Imeguras Nov 30 '20 at 02:12