PDA

View Full Version : SCANF seems confused or is it me?




henrybotha
Apr 19, 2012, 02:48 AM
I am trying to get started with Objective-C and want to write a simple mathematical calculator. I am running OSX Lion with latest updated X-Code.

Ask the user for 3x inputs with scanf. 2x inputs = integers. 1x input = char(computational operator ie "*","+","/","-").

I message a function with these input values, but when I print the values in the function, the 2nd int(actually the last int just before sending the char) is zero.

My code is as follows:

int num1 = 0;
int num2 = 0;
char comptOperator;

NSLog(@"Number one:");
scanf("%d",&num1);
NSLog(@"Number two:");
scanf("%d",&num2);
NSLog(@"Enter computational operator");
scanf("%s",&comptOperator);

doCalculation(num1,num2,comptOperator);



The doCalculation function is declared as follows:



void doCalculation (int x,int y, char z) {

NSLog(@"You are about to do the following calculation on numbers %i and %i and use the following operator %c",x,y,z);



The doCalcuation function produces the following output when I enter 4,6,*:
You are about to do the following calculation on numbers 4 and 0 and use the following operator *

If I remove num2, and simply pass num1 and the comptOperator then the function returns the following output when I enter 4,*:
You are about to do the following calculation on numbers 0 and use the following operator *.

Everything works fine when:
1.) I dont use scanf, but manually *hardcode* the values passed to the function.
2.) Dont use scanf to get comptOperator (thinking this format string is probably incorrect?)
3.) I input with scanf (in this order) num1,comptOperator,num2

I dont find the logic in this and have been debugging the daylights out of it...can anyone assist please? ie tell me that this is not programmable or what the correct format string is to scanf for this comptOperator - I don't find the *correct" format string from the developer.apple...forums or google search.



Amazing Iceman
Apr 19, 2012, 03:01 PM
Why did you define comptOperator as an int? Shouldn't it be a string or char?

int comptOperator;

Also, try displaying each one of your variables before calling the function; try using printf instead of NSLog. I think there's were the problem occurs.


BTW, You posted your question in the wrong forum. There's a chance the Admin may move it to the correct forum once he discovers it.

chown33
Apr 19, 2012, 03:15 PM
I have to ask: Did you read the reference doc for scanf? If so, exactly which reference doc (man page, in a book, something online)?

If you've read the reference doc for scanf, then you'd see there are two conversion characters that might work: s and c**. Neither of these conversions, however, takes an argument that is a pointer to an int.


** There is a third conversion using [ to define a set of accepted chars, but you might want to ignore that one until you have s and c figured out first.

henrybotha
Apr 20, 2012, 01:50 AM
Why did you define comptOperator as an int? Shouldn't it be a string or char?

int comptOperator;

Also, try displaying each one of your variables before calling the function; try using printf instead of NSLog. I think there's were the problem occurs.

Apologies, the comptOperator is initialized as char in my app - was a typo when I posted. You will see that scanf assigns the value as follows scanf("%s",comptOperator);

Thank you for suggesting printf, I will give that a try.

BTW, You posted your question in the wrong forum. There's a chance the Admin may move it to the correct forum once he discovers it.

Thank you for replying even though I posted in the wrong forum. Can you(or Admin or anyone else) direct me to the correct forum please?

henrybotha
Apr 20, 2012, 02:02 AM
I have to ask: Did you read the reference doc for scanf? If so, exactly which reference doc (man page, in a book, something online)?

If you've read the reference doc for scanf, then you'd see there are two conversion characters that might work: s and c**. Neither of these conversions, however, takes an argument that is a pointer to an int.


** There is a third conversion using [ to define a set of accepted chars, but you might want to ignore that one until you have s and c figured out first.

chown33, I read up quite a bit on scanf - the latest was this link: http://www.cplusplus.com/reference/clibrary/cstdio/scanf/

I actually made a typo when posting(I've edited my post to reflect the application I've written). The comptOperator is actually initialized as char and scanf receives the value as %s.

I have noted that the order I used with scanf - num1,num2,comptOperator is important as this makes or breaks the code.

If I scanf in this order num1,comptOperator,num2 and then message the function. EVERYTHING works perfectly. BUT scanf in this order num1,num2,comptOperator then messaging the function returns a 0 for num2!

My end-goal is to learn objective-c for programming Iphone/Ipad applications - I expected a good base would help me later, but I just don't see the mistake here and not sure I want to spend more time on this issue.

chown33
Apr 20, 2012, 02:35 AM
Post the exact code that works. Descriptions can be misinterpreted. Code is code.

Also post an exact description of what you enter when it doesn't work. List every key pressed, including each RETURN, SPACE-BAR, or ENTER.

Finally, read this reference:
http://developer.apple.com/library/ios/#DOCUMENTATION/System/Conceptual/ManPages_iPhoneOS/man3/scanf.3.html
and reread the one you posted.

Pay careful attention in the reference docs to how whitespace is interpreted in both the format string and the input. I.e. if you have whitespace in the input (e.g. space-bar or RETURN or NEWLINE), but you DON'T have a whitespace character in the format string, what will happen? Where would you need to put a whitespace char in the format string to have it correctly handle ignorable whitespace in the input (e.g. you want to ignore newlines)? If you're not sure, make a test case (write a program with whitespace at specific places in the format string, and then enter input values with whitespace in specific places.

gnasher729
Apr 20, 2012, 02:38 AM
First, answering the question _who_ is confused: Just consider what is more likely, that a function used by thousands of programmers doesn't work as intended, and you are the first who notices, or that you made a mistake. That is the very first step when fixing a problem: _You_ made a mistake and the only question is _which_ mistake.

Second, what do you mean by "typo in your posting"? If you look for a problem, copy the code in XCode and paste it into your post. And editing the post afterwards without telling what you edited is very bad style indeed, because someone might have posted what is wrong with your code and that problem isn't there (anymore).

Third, learn to use the project settings in XCode. If you turn the right warnings on, the compiler will tell you exactly where your mistake is. Without those warnings, just think about what the difference between %s and %c would be.

henrybotha
Apr 20, 2012, 02:47 AM
Post the exact code that works. Descriptions can be misinterpreted. Code is code.

Also post an exact description of what you enter when it doesn't work. List every key pressed, including each RETURN, SPACE-BAR, or ENTER.

Finally, read this reference:
http://developer.apple.com/library/ios/#DOCUMENTATION/System/Conceptual/ManPages_iPhoneOS/man3/scanf.3.html
and reread the one you posted.

Pay careful attention in the reference docs to how whitespace is interpreted in both the format string and the input. I.e. if you have whitespace in the input (e.g. space-bar or RETURN or NEWLINE), but you DON'T have a whitespace character in the format string, what will happen? Where would you need to put a whitespace char in the format string to have it correctly handle ignorable whitespace in the input (e.g. you want to ignore newlines)? If you're not sure, make a test case (write a program with whitespace at specific places in the format string, and then enter input values with whitespace in specific places.

Thank you, I really appreciate your suggestions. I will post my findings once I have the data and have read these documents again..and again..

Regards

----------

gnasher, all very valid points!

I've taken chown33's suggestions very seriously and will do more homework. I will post my findings after I have more data.

First, answering the question _who_ is confused: Just consider what is more likely, that a function used by thousands of programmers doesn't work as intended, and you are the first who notices, or that you made a mistake. That is the very first step when fixing a problem: _You_ made a mistake and the only question is _which_ mistake.

I have little doubt that this was something I overlooked, but for the life of my - I couldn't find the mistake.

Second, what do you mean by "typo in your posting"? If you look for a problem, copy the code in XCode and paste it into your post. And editing the post afterwards without telling what you edited is very bad style indeed, because someone might have posted what is wrong with your code and that problem isn't there (anymore).

I edited my post with the following explanation:
Last edited by henrybotha; Today at 08:45 AM. Reason: I made a type in posting my code. comptOperator is not an int in my app, by a char. The function I message also receives char


Third, learn to use the project settings in XCode. If you turn the right warnings on, the compiler will tell you exactly where your mistake is. Without those warnings, just think about what the difference between %s and %c would be.

Good suggestion-I will learn how to properly configure my XCODE.

henrybotha
Apr 20, 2012, 06:59 AM
Post the exact code that works. Descriptions can be misinterpreted. Code is code.

Also post an exact description of what you enter when it doesn't work. List every key pressed, including each RETURN, SPACE-BAR, or ENTER.

Finally, read this reference:
http://developer.apple.com/library/ios/#DOCUMENTATION/System/Conceptual/ManPages_iPhoneOS/man3/scanf.3.html
and reread the one you posted.

Pay careful attention in the reference docs to how whitespace is interpreted in both the format string and the input. I.e. if you have whitespace in the input (e.g. space-bar or RETURN or NEWLINE), but you DON'T have a whitespace character in the format string, what will happen? Where would you need to put a whitespace char in the format string to have it correctly handle ignorable whitespace in the input (e.g. you want to ignore newlines)? If you're not sure, make a test case (write a program with whitespace at specific places in the format string, and then enter input values with whitespace in specific places.

For what is it worth, I have found that scanf actually ignores most whitespaces, but whitespace within input 1 seems to affect input 2 "Generally skips input 2".

I am seriously parking this issue and moving on, but if anyone else would like to discuss/comment/explore further. I will be available.

I have posted my code below that works and the code that doesn't. Below each snippet is also the console output copied from xcode. after any char,int entered I pressed the enter button to accept.

!!!=====THIS CODE BLOCK WORKS=====!!!!


#import <Foundation/Foundation.h>

void doCalculation (int x,int y,char z) {
NSLog(@"You are about to do the following calculation on numbers %i and %i and use the following operator %c",x,y,z);
}

int main(int argc, const char * argv[])
{
@autoreleasepool {

int num1 = 0;
int whatevernumber = 0;
char comptOperator;

NSLog(@"Operator");
scanf("%s",&comptOperator);

NSLog(@"Number one:");
scanf("%d",&num1);

NSLog(@"Number two:");
scanf("%d",&whatevernumber);

doCalculation(num1,whatevernumber,comptOperator);

}
return 0;
}



//CONSOLE
2012-04-20 13:17:24.963 tutorial[13221:403] Operator
1 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:17:27.235 tutorial[13221:403] Number one:
2 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:17:27.622 tutorial[13221:403] Number two:
3 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:17:28.011 tutorial[13221:403] You are about to do the following calculation on numbers 2 and 3 and use the following operator 1

//CONSOLE
2012-04-20 13:20:11.724 tutorial[13255:403] Operator
/ "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:20:15.070 tutorial[13255:403] Number one:
20 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:20:16.431 tutorial[13255:403] Number two:
40 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:20:17.889 tutorial[13255:403] You are about to do the following calculation on numbers 20 and 40 and use the following operator /

//CONSOLE
2012-04-20 13:20:32.557 tutorial[13260:403] Operator
%^ "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:20:35.542 tutorial[13260:403] Number one:
302 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:20:38.102 tutorial[13260:403] Number two:
323 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:20:41.566 tutorial[13260:403] You are about to do the following calculation on numbers 302 and 323 and use the following operator %

//CONSOLE
2012-04-20 13:21:01.808 tutorial[13265:403] Operator
# "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:21:04.688 tutorial[13265:403] Number one:
123 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:21:06.712 tutorial[13265:403] Number two:
321 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:21:08.020 tutorial[13265:403] You are about to do the following calculation on numbers 123 and 321 and use the following operator #


!!!=====THIS CODE BLOCK DOESN'T WORK=====!!!!
!!!=====ONLY THING THAT HAS CHANGED=====!!!!!
!!!=====IS THE ORDER IN WHICH scanf=======!!!!
!!!=====IS CALLED AND INPUT ASSIGNED======!!!
!!!=====TO VARIABLES=================!!!!

// main.m
// tutorial
//
// Created by Henry Botha on 2012/04/18.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>

void doCalculation (int x,int y,char z) {
NSLog(@"You are about to do the following calculation on numbers %i and %i and use the following operator %c",x,y,z);
}

int main(int argc, const char * argv[])
{
@autoreleasepool {

int num1 = 0;
int whatevernumber = 0;
char comptOperator;


NSLog(@"Number one:");
scanf("%d",&num1);

NSLog(@"Number two:");
scanf("%d",&whatevernumber);

NSLog(@"Operator");
scanf("%s",&comptOperator);

doCalculation(num1,whatevernumber,comptOperator);

}
return 0;
}



//CONSOLE
2012-04-20 13:22:00.274 tutorial[13285:403] Number one:
2 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:22:09.798 tutorial[13285:403] Number two:
3 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:22:11.660 tutorial[13285:403] Operator
1 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:22:12.540 tutorial[13285:403] You are about to do the following calculation on numbers 2 and 0 and use the following operator 1


//CONSOLE
2012-04-20 13:22:33.590 tutorial[13301:403] Number one:
20 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:22:39.266 tutorial[13301:403] Number two:
40 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:22:41.747 tutorial[13301:403] Operator
/ "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:22:42.707 tutorial[13301:403] You are about to do the following calculation on numbers 20 and 0 and use the following operator /

//CONSOLE
2012-04-20 13:23:00.485 tutorial[13310:403] Number one:
302 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:23:04.870 tutorial[13310:403] Number two:
323 "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:23:08.730 tutorial[13310:403] Operator
%^ "no leading/trailing spaces, pressed enter to accept"
2012-04-20 13:23:12.746 tutorial[13310:403] You are about to do the following calculation on numbers 302 and 94 and use the following operator %

gnasher729
Apr 20, 2012, 11:59 AM
As I said, %s vs. %c.

I recommend you google for "n1256" and download the file n1256.pdf which contains the last draft for the C99 standard, which applies to the C portions of Objective-C as well. Or you could google for "scanf format specifiers" which should explain the difference between %s and %c.

chown33
Apr 20, 2012, 01:28 PM
For what is it worth, I have found that scanf actually ignores most whitespaces, but whitespace within input 1 seems to affect input 2 "Generally skips input 2".

...
scanf("%d",&num1);

I don't have the time to test the code right now. I'll just say there's a difference between this:
scanf("%d",&num1);

and this:
scanf(" %d ",&num1);

Note the leading and trailing space characters in the format string. In fact, any whitespace character will work: \t \n \r. Personally, I usually write scanf formats with \t in them, because it's easier to see when rapidly scanning the source.

Knowing exactly how scanf handles whitespace is crucial. The absence of whitespace in the format strings for the posted code suggests that this important aspect of scanf has not yet been adequately comprehended.

It may seem that scanf is a nice simple beginner-friendly way to obtain free-form input and do conversions. However, the scanf functions can be subtle nearly to the point of being devilish, especially when it comes to whitespace, line-handling, and limiting the length of input strings.


EDIT

I tested the non-working code. The problem lies in the use of %s with a single char. I'd previously thought it might be the "%d" conversions, but that wasn't it.

tl;dr: %s vs. %c.

Referring to the man page for scanf (http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/fscanf.3.html), the description of the s conversion (red hilite added):
Matches a sequence of non-white-space characters; the next pointer must be a pointer to char, and the array must be large enough to accept all the sequence and the terminating NUL character. The input string stops at white space or at the maximum field width, whichever occurs first.

Since the destination was a single char, there is no room for a terminating NUL character. As a result, there was a memory overrun when %s was used. Because the variable comptOperator was adjacent to the int variable whatevernumber, that happened to be where the NUL terminator was stored. The value of the terminator is 0x00, which is why the output was:
2012-04-20 13:22:42.707 tutorial[13301:403] You are about to do the following calculation on numbers 20 and 0 and use the following operator /

The fix is to change this:
scanf("%s",&comptOperator);

to this:
scanf(" %c",&comptOperator);

The leading whitespace is required, otherwise you'll read the newline from the previous input line. You want to skip leading whitespace, then read exactly one non-whitespace character. I didn't even have to venture into deep dark secret territory to discover this. It's simply the information given in the description of the c conversion, on scanf's man page:
Matches a sequence of width count characters (default 1); the next pointer must be a pointer to char, and there must be enough room for all the characters (no terminating NUL is added). The usual skip of leading white space is suppressed. To skip white space first, use an explicit space in the format.

RTFM FTW.