LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   My first C-program - scanf() does not what I want... (https://www.linuxquestions.org/questions/programming-9/my-first-c-program-scanf-does-not-what-i-want-4175445557/)

Lennie 01-14-2013 11:04 AM

My first C-program - scanf() does not what I want...
 
I'm trying to learn C, and writing my first small program. I have this function (in a bigger program):

Code:

char player_move(char *raw_data, char player){
        int position = 0;
        while (! position){
                printf("\n%c Go to: \n", player);
                scanf("%d", &position);
                if ( position <  1 || position > 9 || raw_data[(position - 1)] != ' '){
                    printf("Choose a free place on board.\n");
                    position = 0;
                }
        }
        raw_data[position -1] = player;
        return 0;
}

If I input a number it behaves as expected, but if I input a letter, then it goes into an infinite loop and outputs

Code:

X Go to:
Choose a free place on board.

X Go to:
Choose a free place on board.

again and again for ever, without waiting for input. What should I do to get the same error and a new chance to input a number, as if I input a wrong number?

NevemTeve 01-14-2013 11:32 AM

That's why you shouldn't use scanf. fgets and sscanf are you friends. But do check the return value of (s)scanf, it is important.

Lennie 01-14-2013 12:21 PM

Quote:

Originally Posted by NevemTeve (Post 4869787)
That's why you shouldn't use scanf. fgets and sscanf are you friends. But do check the return value of (s)scanf, it is important.

How do I check the return value?
I did some googling for sscanf and fgets, but I get the impression they are for strings. How do I convert them to integer? I'm completely new at this...

johnsfine 01-14-2013 01:00 PM

Quote:

Originally Posted by Lennie (Post 4869773)
If I input a number it behaves as expected, but if I input a letter, then it goes into an infinite loop

Since I never use scanf, I don't recall what you are supposed to do to flush the pending unusable input. But you need to do something.

scanf only reads what fits the specification you gave it. It can get stuck on something that doesn't fit and keep reading nothing, because nothing valid precedes the unreadable pending input.

When the user has given you wrong input, you should first flush the pending input, then display a prompt for the user to try again, then try scanf again.

johnsfine 01-14-2013 01:05 PM

Quote:

Originally Posted by Lennie (Post 4869825)
I did some googling for sscanf and fgets, but I get the impression they are for strings. How do I convert them to integer? I'm completely new at this...

The idea was that you use fgets to move a whole line of input from the input source to a char buffer, then you use sscanf to get information from that text buffer the same way you might have used scanf to get information from the original input.

That makes your program automatically aware of the line boundaries in the input.

If you don't do that, then any scanf you try might be trying to read something from remains of a line that was typed for an earlier scanf.

Flushing any pending input after each answer (before the next prompt) is an alternative that lets you address the same problem without using fgets+sscanf. But that might mess the user up if the user types ahead, and it really messes you up if the input is coming from a script rather than an interactive user.

You can't read the user's mind, so there is no 100% bullet proof way to find where one answer ends and the next begins. Give some thought to what behaviors you want for which cases of correct and incorrect user input.

The model that each line is one answer, is a pretty good model and combining fgets+sscanf rather than directly using scanf is an easy way to force one line equals one answer.

Flushing pending input after any wrong answer, but not after a correct answer, is another choice that is often a good fit to the range of situations you would like to cover.

NevemTeve 01-14-2013 02:18 PM

> How do I check the return value?

eg:
Code:

if (sscanf (str, "%d", &n) != 1) { /* failed */
> I did some googling for sscanf and fgets, but I get the impression they are for strings.

fgets reads a line as a string, then sscanf parses that string

theNbomr 01-14-2013 04:19 PM

Quote:

Originally Posted by NevemTeve (Post 4869921)
fgets reads a line as a string, then sscanf parses that string

Just to expand on that whole concept a bit...

There is plain old scanf(), which reads from standard input (normally your console/keyboard). Then there is sscanf(), which reads from the string variable that is specified in its first argument, rather than scanning stdin like its cousin scanf(). There is also the sibling fscanf(). Like sscanf(), it has an additional first argument, a FILE *, allowing you to specify an open file from which to scan input. All three forms of the scanf() family obey the same rules for the use of the format string and conversion of input character data to other data types.
Don't wory about it for now, but there is also the vscanf(), vsscanf(), and vfscanf() functions with the same consistent nature. For output, the printf() family has the same consistent nomenclature.

--- rod.

H_TeXMeX_H 01-15-2013 03:19 AM

I agree with the above. I know they always teach C programming students to use scanf, but avoid it if you value your sanity. Use fscanf+sscanf. Read a string with fscanf from stdin, then parse it using sscanf. You can also use fgets instead of fscanf, it's your choice.

Lennie 01-15-2013 10:57 AM

After som more reading, and some trial and error I now have this function:
Code:

char player_move(char *raw_data, char player){
    int position = 0;
    char buffer[256];
    while (! position){
        printf("\n%c Go to: \n", player);
        fgets(buffer, sizeof(buffer), stdin);
        sscanf(buffer, "%d", &position);         
            if ( position <  1 || position > 9 || raw_data[(position - 1)] != ' '){
                printf("Choose a free place on board.\n");
                position = 0;
        }
    }
    raw_data[position -1] = player;
    return 0;
}

It seems to work. I have tried to input all kind of nonsense I can think of, and so far no strange behavior.

But at the same time, this revealed another problem. I had scanf() in the main function, wich seemed to work before, but for some reason it started to behave strange after this function was fixed. So I could need some help to understand how to fix that one also.

This is what I have now, but "it doesn't work" - if I enter 'y' it doesn't begin another game, but the program stops with exitcode 0.
Code:

int main(void) {
    welcome();
    char raw_data[] = "        ";
    display_board(raw_data);
    int player_int = 1;
    int game_over = 0;
    char again;
    char input[256];
    while (! game_over){
        one_round(player_int);
        printf("Do you want to play again? [y/n] \n");
        fgets(input, sizeof(input), stdin);
        sscanf(input, "%s", &again);
       
        if (again == "y"){
            player_int *= -1;
        }
        else {
            game_over = 1;
        }
  }
    return 0;
}

I get this error from the compiler in Geany
Code:

tictactoe.c:29:19: warning: comparison between pointer and integer [enabled by default]
tictactoe.c:29:19: warning: comparison with string literal results in unspecified behavior [-Waddress]

Edit: The error is about the line: 'if (again == "y"){'.

johnsfine 01-15-2013 11:46 AM

Quote:

Originally Posted by Lennie (Post 4870674)
Code:

    char again;
...
        sscanf(input, "%s", &again);
       
        if (again == "y"){


You need to be consistent about the data type of again.

If it is a char, you need to parse it with %c, not %s and compare it to 'y' not "y"

If again were a char array used as a null terminated string, you could parse it with %s, but when you compare it to "y", you would need to use strcmp, not ==

Lennie 01-17-2013 10:07 AM

Thanks, I think I got it working now.

The relevant part of the code:
Code:

char input[512];
char again[512];

fgets(input, sizeof(input), stdin);
sscanf(input, "%s", &again);
if ( (strcmp(again, "y") == 0) || (strcmp(again, "yes") == 0) || strcmp(again, "Y") == 0)){
    player_int *= -1;
}



All times are GMT -5. The time now is 06:17 AM.