LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 01-14-2013, 11:04 AM   #1
Lennie
Member
 
Registered: Aug 2012
Location: Sweden
Distribution: LFS, built with pacman
Posts: 374

Rep: Reputation: 85
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?
 
Old 01-14-2013, 11:32 AM   #2
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,856
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
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.
 
Old 01-14-2013, 12:21 PM   #3
Lennie
Member
 
Registered: Aug 2012
Location: Sweden
Distribution: LFS, built with pacman
Posts: 374

Original Poster
Rep: Reputation: 85
Quote:
Originally Posted by NevemTeve View Post
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...
 
Old 01-14-2013, 01:00 PM   #4
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by Lennie View Post
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.
 
Old 01-14-2013, 01:05 PM   #5
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by Lennie View Post
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.

Last edited by johnsfine; 01-14-2013 at 01:14 PM.
 
Old 01-14-2013, 02:18 PM   #6
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,856
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
> 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
 
Old 01-14-2013, 04:19 PM   #7
theNbomr
LQ 5k Club
 
Registered: Aug 2005
Distribution: OpenSuse, Fedora, Redhat, Debian
Posts: 5,399
Blog Entries: 2

Rep: Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908Reputation: 908
Quote:
Originally Posted by NevemTeve View Post
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.
 
Old 01-15-2013, 03:19 AM   #8
H_TeXMeX_H
LQ Guru
 
Registered: Oct 2005
Location: $RANDOM
Distribution: slackware64
Posts: 12,928
Blog Entries: 2

Rep: Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301Reputation: 1301
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.
 
Old 01-15-2013, 10:57 AM   #9
Lennie
Member
 
Registered: Aug 2012
Location: Sweden
Distribution: LFS, built with pacman
Posts: 374

Original Poster
Rep: Reputation: 85
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"){'.

Last edited by Lennie; 01-15-2013 at 11:13 AM.
 
Old 01-15-2013, 11:46 AM   #10
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by Lennie View Post
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 ==

Last edited by johnsfine; 01-15-2013 at 11:49 AM.
 
Old 01-17-2013, 10:07 AM   #11
Lennie
Member
 
Registered: Aug 2012
Location: Sweden
Distribution: LFS, built with pacman
Posts: 374

Original Poster
Rep: Reputation: 85
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;
}
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Simple C program using scanf/printf cryingthug Programming 6 06-26-2012 08:49 PM
Using scanf in c on doubles mkrems Programming 2 04-28-2008 07:10 PM
scanf() problem. LUB997 Programming 3 12-19-2004 08:03 PM
scanf blackzone Programming 1 08-04-2004 01:50 AM
scanf help homerz Programming 2 11-11-2003 05:48 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 11:03 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration