Why ‘exactly’ does this code loop endlessly if you enter a non number character?
The first question comes about because I want to learn good defensive coding. Does anyone know a good way to check user input? My google-fu failed me. Some people seemed to be of the opinion that if I specify %f in scanf that I am ‘demanding’ a float; I verified this, in a way, by printing the value of userInput. In fact, if I comment out the do while loop, there is ‘no problem’ with the execution of the code. It assigns a 0 to userInput and goes about its business.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
float userInput;
float result;
NSLog(@"3X^3 -5x^2 + 6");
do {
NSLog(@"What is x?");
scanf("%f", &userInput);
NSLog(@"userInput = %f", userInput);
} while(userInput == 0);
result = 3 * (userInput * userInput * userInput) - 5 * (userInput * userInput) + 6;
NSLog(@"the result is: %f", result);
[pool drain];
return 0;
}
This is really nothing to do with Objective-C or Cocoa. The issue is simply to do with the use of the standard C library function
scanf, and handling the error condition. From thescanfmanpage, describing the return code:A valid numeric input can be parsed by
scanfwith the%fspecifier, so that obviously works as expected. But if you enter in a non-numeric character,scanfcannot convert this to a float, and leaves the text in the buffer ofstdin. Since the code is not checking the return code fromscanf, and only testing ifuserInputis non-zero, the loop will never exit, asuserInputhappens to start at0.0, and will never be updated asscanfwill not pull the non-numeric characters out of thestdinbuffer. So that is why your code runs in an infinite loop.If you had initialized
userInputto a non-zero value, that would fix the problem one way, as non-numeric input would causescanfto fail and thewhilecondition would be triggered. But a better fix would be to check the return code ofscanf. If it is zero, print an error message, and do afpurge(stdin)to clear out the invalid input before you loop around again, like this:So this is the plain C approach to input and parsing. The bottom line for defensive coding is that you should always check the return code!
As Chris mentions, for an actual Cocoa application, you would want to look at
NSNumberFormatterand the like, but then you would presumably be taking input from widgets rather than file streams, so the code would be quite different to the above.