usage of the return value of scanf - c

I am learning C from a book and I am starting with loop instructions. But there is a sample code which I could not understand.
Can anyone tell me why author has used status = scanf("%ld", &num); ? Why there is a = with scanf ?
/* summing.c -- sums integers entered interactively */
#include <stdio.h>
int main(void)
{
long num;
long sum = 0L; /* initialize sum to zero */
int status;
printf("Please enter an integer to be summed ");
printf("(q to quit): ");
status = scanf("%ld", &num);
while (status == 1) /* == means "is equal to" */
{
sum = sum + num;
printf("Please enter next integer (q to quit): ");
status = scanf("%ld", &num);
}
printf("Those integers sum to %ld.\n", sum);
return 0;
}

Because scanf() returns a value indicating how well the string matched the format passed, i.e. how many parameters were successfuly filled with data.
You would know that if your read some kind of manual page, or the standard, if you ever encounter a function that you don't know, always read about it as much as you can, so you can understand how to use it.

scanf returns the number of successfully matched items from its specifier list. So, if you enter "q", scanf will return 0 (since "q" is not an integer), and the loop will terminate.

Please have a look at the man page of scanf(). It returns the number of items successfully scanned. So the return value is used to check the successfulness of scanf().
In your code,
status = scanf("%ld", &num);
// code
while (status == 1)
the return value of scanf() is stored into status and checked later to check whether scanf() has successfully scanned 1 item or not.
If it has returned 1, meaning a long int has been entered. Then the while() loop will continue, asking for the next input.
One you enter q, it won't match the coversion specifier %ld, hence scanf() will fail, hence it will return 0. Then the while() loop will terminate.

Related

Why is Char Data Type used in this Function?

I'm a student and was given this function by our teacher to get user input. It's used for integers but I'm confused why there's a char data type. If someone could explain this function that would be great.
#pragma warning (disable: 4996)
int getNum(void)
{
char record[121] = {0};
int number = 0;
fgets(record, 121, stdin);
if( sscanf(record, "%d", &number) != 1)
{
number = -1;
}
return number;
}
When calling we do this for example:
int age = 0; //we always have to initialize
age = getNum(); //this is how we call the function to get user input
The char record[121] variable is actually an array of characters, otherwise known as a string. It is used here so that, if an erroneous input is given (instead of a valid integer input), then the input stream is still cleared by the fgets() call, which takes in all characters up to (and including) the newline character.
The sscanf function then attempts to extract a valid integer from the read character string and, if it fails, then an error value (-1) is assigned to signal that fact. If it succeeds, it assigns the given input value to number, in the same way that a successful call to scanf("%d", &number) would.
However, just using such a simple scanf() call would potentially leave 'unprocessed' characters in the input stream, which could cause problems for any subsequent input operations (or require the input stream to be cleared).
Using such a technique will also prevent undesired/unexpected effects caused by a user typing extra characters after a valid input. For example, the following (deliberately bad) code will cause a 'surprise' if you enter 1a in response to the first prompt:
#include <stdio.h>
int main()
{
int number = 0, number2 = 0;
printf("Enter first number: ");
scanf("%d", &number);
printf("Enter second number: ");
scanf("%d", &number2);
printf("Given numbers were: %d and %d.\n", number, number2);
return 0;
}
However, using the getNum() function your teacher provided, the following code is much more robust:
int main()
{
int number = 0, number2 = 0;
printf("Enter first number: ");
number = getNum();
printf("Enter second number: ");
number2 = getNum();
printf("Given numbers were: %d and %d.\n", number, number2);
return 0;
}
The program is reading lines from a file with fgets, which uses a string as storage buffer for the line. After that it processes the line to find a string that can be read as an int (%d), checks if that operation went OK, and returns the integer if so.
The function is reading input as text, storing it to the character array record. It then converts that string into an equivalent integer value using sscanf.
This is a bit safer than just using scanf directly - it ensures that if the user types non-numeric input, it's still consumed from the input stream so that it doesn't cause problems later on.

Analyze a variable in C and then decide what to do

Hi I'm still a C beginner and a beginner coder,
I've been trying to develop some sort of a rudimental interface via command line and would like to implement that when the user inserts something that isn't a number the loop breaks and moves on
(my dream would be that it checks when the input is equal to the word new) Anyhow here's my code snippet:
do {
printf("\nInsert the score you obtained: ");
scanf("%d", &avg);
} while ( isdigit(avg) == 0 );
Basically until the input is a number it will keep going and when something that isn't an integer is inserted it should exit the loop.
To receive something that isn't an integer, you have to scanf something that isn't an integer.
char* input;
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", input);
...
Now that you have a variable that can hold both "quit" and some number (represented as characters), you need to make a decision based on what kind of input you received.
char input[80];
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", &input);
while ( strncmp(input, "exit", 4) == 0 );
this means also that you'll have to possibly convert the "string" of the number into a number value.
char input[80];
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", &input);
int number = convert_to_number(input);
while ( strncmp(input, "exit", 4) == 0 );
but the conversion should only happen if it looks like a number
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", &input);
if ( looks_like_number(input) ) {
int number = convert_to_number(input);
}
} while ( strncmp(input, "exit", 4) == 0 );
and finally you might need to collect multiple inputs, as the loop will accept them now
do {
printf("\nInsert the score you obtained, or exit to quit: ");
scanf("%[^/n]", &input);
if ( looks_like_number(input) ) {
sum = add(sum, convert_to_number(input));
}
} while ( strncmp(input, "exit", 4) == 0 );
This is only one solution of a very large number of possible ways to do this.
This solution also could be improved (like trimming white space around the "exit" flag, so a user could type " exit" or "exit " to leave. One could also make the loop not a loop, as the question seems to imply entering one number or "exit" once, while a loop will permit many numbers until exit was typed. And one could remove the silly add function which was only used to demonstrate with a word a very clear idea, when + might do.
Even if the above loop / approach isn't a perfect fit for your need, please look at it carefully because the ideas within it can be used to make your program a success.
Also, don't use isdigit for checking for an entire number. Look into sscanf which is like printf but for reading stuff out of strings. sscanf your number off the string, and check the return value to see if you found a number within the string.
isdigit (prototyped as int isdigit (int Test_Character);) is not the right function to use with testing whether an int is a digit or a number. isdigit() is used rather to test if a single character is a decimal digit (0-9). The input to this function must be a character with integer value between 0 and 255. ( refer to ASCII table to view what these value are.)
The right thing to do here is to determine if scanf converted an input value correctly.
The function scanf() is prototyped with a return value, indicating success or failure, but your code does not currently check it.
So, in summary, your while loop can be written using the return of scanf():
int main(void)
{
int count = 0;
int avg= 0;
do
{
printf("\nInsert the score you obtained: ");
count = scanf("%d", &avg);
}while(count > 0); //stays in loop for any numeric value
// } while ( isdigit(avg) == 0 );//Note; input containing decimal points will be truncated
//to contain only integer portion
return 0;
}
As long as the input is an integer numeric value, scanf will return count as a positive value (the number of values scanned correctly.)
For example:
123, -123, 8, 0 will all allow the loop to continue.
a, dfg will all result in a failed attempt to convert the input to a numeric, and return EOF (-1)
Process as string representation of numeric: (similar behavior with alternative coding approach.)
I promised to show how to use strtol():
By changing the input format specifier from "%d" to "%s", scanf() will read the input value of 123 as a string: "123". When input is brought into your program as a string representation of a numeric, it must be converted to a number a numeric type before it can be used. strtol() can be used to do this conversion.
Here is a modification of your original program illustrating how it can be done:
int main(void)
{
int count = 0;
char avg[20] = {0};//changed to string variable
long val = 0;
bool success = FALSE;
do
{
printf("\nInsert the score you obtained: ");
count = scanf("%s", avg);//changed format specifier to read strings
if(count)
{
success = parseLong(avg, &val);//see correct usage of strtol in function
}
}while(success); //Stays in loop for any numeric input.
//Note: input containing decimal values
//will be truncated to contain integer portion only.
return 0;
}
bool parseLong(const char *str, long *val)
{
char *temp;
BOOL rc = TRUE;
errno = 0;
*val = strtol(str, &temp, 0);
if (temp == str || ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE))
rc = FALSE;
return rc;
}

Validate integer against chars

I'm trying to make a program where the user inputs value to an array. What is actually required is that the program should validate against a char character. So if the user inputs a random char such as 'n' the program should tell him "You introduced a char, please input an integer: ".
How is that possible to make that without using a char variable?
for (i = 1; i <= size; i++) {
printf("Introduce the value #%d of the list: ", i);
scanf("%d", &list[i]);
if () { // I'm blocked right in this line of code.
printf("What you tried to introduce is a char, please input an integer: ");
scanf("%d", &list[i]);
}
Thanks in advance.
As #MFisherKDX says, check the return value of scanf. From the scanf man page:
These functions return the number of input items successfully matched
and assigned, which can be fewer than provided for, or even zero in
the event of an early matching failure.
The value EOF is returned if the end of input is reached before either
the first successful conversion or a matching failure occurs. EOF is
also returned if a read error occurs, in which case the error
indicator for the stream (see ferror(3)) is set, and errno is set
indicate the error.
So capturing the return value of scanf in an int variable and then comparing that variable to 1 (in your case, because you are only attempting to read 1 item) should tell you if scanf successfully read an integer value.
However, there is a nasty pitfall when using scanf that you should be aware of. If you do type n at the prompt, scanf will fail and return 0, but it will also not consume the input you typed. Which means that the next time you call scanf, it will read the same input (the n character you typed), and fail again. And it will keep doing so no matter how many times you call scanf. It always amazes me that computer science educators continue to teach scanf to students, given not only this potential pitfall, but several other pitfalls as well. I wish I had a nickel for every hour that some CS student somewhere has spent struggling to get scanf to behave the way their intuition tells them it should. I'd be retired on my own private island by now. But I digress.
One way around this particular pitfall is to check if scanf failed, and if so, to purposely consume and discard all input from stdin up to and including the next newline character or EOF, whichever comes first.
First let's look at some unfixed code that causes an infinite loop if you enter a non-integer as input:
// Typing the letter 'n' and hitting <Enter> here causes an infinite loop:
int num, status;
while (1) {
printf("Enter a number: ");
status = scanf("%d", &num);
if (status == 1)
printf("OK\n");
else
printf("Invalid number\n");
}
The above code will (after you type n and hit <Enter>), will enter an infinite loop, and just start spewing "Invalid number" over and over. Again, this is because the n you entered never gets cleared out of the input buffer.
There are a few possible ways to get around this problem, but the consensus seems to be that the most portable and reliable way to do so is as follows:
// Fixed. No more infinite loop.
int num, status;
while (1) {
printf("Enter a number: ");
status = scanf("%d", &num);
if (status == 1)
printf("OK\n");
else {
printf("Invalid number\n");
// Consume the bad input, so it doesn't keep getting re-read by scanf
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) ;
if (ch == EOF) break;
}
}
The function scanf() will returns the number of elements read, so in this case it will return 1 every time it reads an int and 0 when it reads a char, so you just need to verify that return value.
Keep in mind that after reading a character it will remain in the buffer so if you use the scanf() command again it will read the character again and repeat the error. To avoid that you need to consume the character with while(getchar() != '\n');
With that in mind I modified your code so that it works properly printing an error message if a character is introduced and asking for a new int.
for (int i = 1; i <= size; i++) {
printf("Introduce the value #%d of the list: ", i);
while (!scanf("%d", &list[i])) { //verifies the return of scanf
while(getchar() != '\n'); //consumes the character in case of error
printf("What you tried to introduce is a char\n");
printf("please introduce the value #%d of the list: ", i);
}
}

printf after scanf always displays 1 (same unexpected value)

Okay so I'm trying to do a basic program in VS. Enter a number then it gets printed out. 1 is always printed.
int main(){
printf("Enter an integer: ");
int n = scanf_s("%d", &n);
printf("%d", n);
}
You are assigning the returned value from scanf_s() to the variable n, that means that the program will print 1 in case a successful read happened.
What you should do is
int numberOfItemsMatched;
int readValue;
numberOfItemsMatched = scanf_s("%d", &readValue);
if (numberOfItemsMatched == 1)
printf("%d\n", readValue);
I hope the variable names are self explanatory, and it's always a good idea to use this kind of names.
return type of scanf is number of items read. so if scanf is succesful in reading an item, it returns one which is assigned to n here. hence the output is 1. So separate declaration of n and scanf.

entering a char using scanf and saving it in an int can cause undefined behaviour?

I am working on a code, and I tried to enter a char instead of integer, and the result was '2' regardless of the character I entered, is it undefined behaviour or some thing else ?
The code:
#include <stdio.h>
int f1(int n);
int f2(void);
int main(void)
{
int t;
printf("Enter a number: ");
scanf("%d", &t);
/* print proper message */
t ? f1(t) + f2() : printf("zero entered.\n");
return 0;
}
int f1(int n)
{
printf("%d ", n);
return 0;
}
int f2(void)
{
printf("entered.\n");
return 0;
}
when I entered a, the result was "2 entered", and when I entered g the result was "2 entered" and when I entered i,h,k,..... the result was the same. What is that?
If scanf() encounters something it cannot parse based on the specified format string, it simply stops and returns early. So it never writes anything to t (you're just seeing whatever indeterminate value t had before the call).
To handle this, you should always examine the return value of scanf.
It is because scanf failed to parse you input. It expects you to enter a decimal digits since you used %d.
You have to check the return value of scanf to avoid this kind of behavior :
On success, the function returns the number of items of the argument list successfully filled. This count can match the expected number of items or be less (even zero) due to a matching failure, a reading error, or the reach of the end-of-file.
So :
int items_matched;
// ...
items_matched = scanf("%d", &t); // Get the return value
if ( items_matched != 1 ) // Check it
{
printf("Matching failure with scanf.\n");
return 0;
}
else
{
if ( t == 0 )
printf("zero entered\n");
else
printf("%d entered\n", t);
}
You don't need your f1(t) + f2() who is quite confusing...
The problem is, as you say, that scanf using %d format is expecting you to enter one or more ascii digits. It stops scanning the input at a non-digit character, so it never reads what you type and t has whatever value it had before the scant was called.
You need to check the return code of scanf. Have a look at the manpage:
Return Value
These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.
The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs, in which case the error indicator for the stream (see ferror(3)) is set, and errno is set indicate the error.
In other words, scanf does not write anything into t, it's uninitialized the whole time.
So, instead of this:
scanf("%d", &t);
try this:
int items_matched = scanf("%d", &t);
if (items_matched != 1) {
printf("bad number entered\n");
exit(1);
}
From the Linux man page
The format string consists of a sequence of directives which describe
how to process the sequence of input characters. If processing of a
directive fails, no further input is read, and scanf() returns. A
"failure" can be either of the following: input failure, meaning that
input characters were unavailable, or matching failure, meaning that
the input was inappropriate.
In the C11 standard description of scanf (7.21.6.4 The scanf function) section 3:
The scanf function returns the value of the macro EOF if an input
failure occurs before the first conversion (if any) has completed.
Otherwise, the scanf function returns the number of input items
assigned, which can be fewer than provided for, or even zero, in the
event of an early matching failure.
Emphasis is mine . So as #oli charlesworth said you should check the return value of scanf when in doubt :)

Resources