ignoring return value (C Programming) [duplicate] - c

This question already has answers here:
Warning: ignoring return value of 'scanf', declared with attribute warn_unused_result
(11 answers)
Closed 4 years ago.
I just started learning C programming a few days back and I'm trying out some problems from the Kattis (open.kattis.com) website. I came up with this problem along the way where I don't really understand what it means.
//two stones
#include <stdio.h>
int main()
{
int n,x=2;
printf("The number of stones placed on the ground is ");
scanf("%d",&n);
if (n%x != 0)
{
printf("The winner of the game is Alice! \n");
}
else
{
printf("The winner of the game is Bob! \n");
}
return 0;
}
This appeared >>
warning: ignoring return value of scanf, declared with attribute warn_unused_result [-Wunused-result]
regarding scanf("%d",&n);
Can anyone explain what's wrong with this and how to rectify this problem? Thanks.

scanf has a return value that indicates success:
C Standard; §7.19.6.4.3:
The scanf function returns the value of the macro EOF if an input failure occurs before
any conversion. 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.
If you have a format string in your call to scanf that has one format specifier, then you can check that scanf succeeded in receiving an input of that type from the stdin by comparing its return value to 1.
Your compiler is warning you about this not specifically because scanf returns a value, but because it's important to inspect the result of scanf. A standard-compliant implementation of printf, for example, will also return a value (§7.19.6.3.3), but it's not critical to the soundness of your program that you inspect it.

You are ignoring the return value of the scanf call.
That is what the compiler warns about and was told to treat as an error.
Please understand that there are many subtle mistakes possible to be done with scanf() and not caring about the success, which is indicated by the return value.
To hide the problem which the compiler kindly notifies you about, I recommend to first try the "obvious" straight forward approach
int IreallyWantToIgnoreTheImportantInfo;
/* ... */
IreallyWantToIgnoreTheImportantInfo = scanf("%d",&n);
This will however only move the problem somewhere else and the valid reason about ignoring the scanf() return value will then probably (or maybe "hopefully") turn into a "variable set but never used" warning.
The proper way to really solve the problem here is to USE the return value.
You could e.g. make a loop, which attempts reading user input (giving an explanation and removing unscanned attempts) until the return value indicates success.
That would probably make much better code, at least much more robust.
If you really really want to ignore, without instead ignoring a variable which contains the info, then try
(void) scanf("%d",&n); /* I really really do not care ... */
But, please take that as completly helpfuly as I mean it, that is not a good idea.

Can someone explain to me what's wrong with this and how to rectify this problem?
Many C functions return values to their callers. C does not require the caller to acknowledge or handle such return values, but usually, ignoring return values constitutes a program flaw. This is because ignoring the return value usually means one of these things is happening:
the function was called in order to obtain its return value, so failing to do anything with that value is an error in itself, or
the function was called primarily for its side effects, but its return value, which conveys information about the function's success in producing those side effects, was ignored. Occasionally the caller really doesn't care about the function's [degree of] success, but usually, ignoring the return value means the program is assuming complete success, such that it will malfunction if that was not actually achieved.
scanf() is ordinarily called for its side effects: reading formatted data from the standard input and recording it in the specified objects. Its return value indicates how many of the given input fields were successfully processed, and if the end of the stream or an I/O error was encountered before parsing any fields, then the return value indicates that, too, via a special return value.
If you do not verify that scanf read all the fields you expected it to do, then you do not know whether it gave you any data to work with, nor can you be confident about the state of the input. For example, suppose that when you run your program, you enter "x" instead of a number. What do you think it will do?
You appear to be using GCC and GLIBC. These are instrumented to produce warnings by default when the return values of certain functions, including scanf, are ignored. This catches many of the more common cases of such flaws. To avoid such warnings, check the return value (appropriately). For example,
if (scanf("%d",&n) != 1) {
fputs("Invalid input -- aborting.\n", stderr);
exit(1);
}

What happen is that your compiler is configured to warn you if you don't check the value returned by scanf.
You have many way to "fix" this :
you can configure your compiler to stop warning you. This is usually a bad idea, but since you're still learning C, it may be counterproductive to focus yourself on the error checking at this step.
You can put the result of scanf in a variable .... and do nothing with it. It will probably fool the compiler. Same as previous, not a good idea ...
You can actually do the error check of scanf. It will be probably confusing since you're learning C, but at last it will be a very good habit to have : each time you call a function that may fail, check if it succeed or failed. To do that, you will have to read the scanf manual : Don't try to read it all, you will probably have an headache before the end. Juste read the "Return Value" section, it's enougth.
Good luck !

What your compiler is warning you about is this:
You are reading input (which you don't control) with scanf(). You tell scanf() that you expect an integer "%d", and scanf() is supposed to place the result of the conversion into the variable you supplied with &n.
Now, what happens when your user does not input an integer, but says "evil message" instead? Well, scanf() cannot convert that into an integer. Accordingly, your variable n will not be initialized. And the only way for your program to realize that something went wrong, is to check what scanf() returns. If it returns that it has made 1 successful conversions, everything's ok. If it returns some other value, you know that some garbage was input into your program. (Usually you would want to bail out with a descriptive error message in case of an error, but details depend on the context)
Failures to handle input errors are among the easiest to exploit security vulnerabilities, and the developers of your compiler know this. Thus, they think that it's generally a really bad idea to ignore the return value of scanf() as it's the only way to catch input errors with scanf(). And they conveniently tell you about this. Try to follow their advise, and make sure that you actually handle the errors that may occur, or prove that they are safe to ignore.

Related

Should I check every single parameter of a function to make sure the function works well?

I notice that the standard c library contains several string functions that don't check the input parameter(whether it's NULL), like strcmp:
int strcmp(const char *s1, const char *s2)
{
for ( ; *s1 == *s2; s1++, s2++)
if (*s1 == '\0')
return 0;
return ((*(unsigned char *)s1 < *(unsigned char *)s2) ? -1 : +1);
}
And many others do not do the same validation. Is this a good practice?
In other library, I saw they check every single parameter, like this:
int create_something(int interval, int mode, func_t cb, void *arg, int id)
{
if (interval == 0) return err_code_1;
if (valid(mode)) return err_code_2;
if (cb == NULL) return err_code_3;
if (arg == NULL) return err_code_4;
if (id == 0) return err_code_5;
// ...
}
Which one is better? When you design an API, would you check all parameters to make it function well or just let it go crash?
I'd like to argue that not checking pointers for NULL in library functions that expect valid pointers is actually better practice than to do error returns or silently ignoring them.
NULL is not the only invalid pointer. There are billions of other pointer values that are actually incorrect, why should we give preferential treatment to just one value?
Error returns are often ignored, misunderstood or mismanaged. Forgetting to check one error return could lead to a misbehaving program. I'd like to argue that a program that silently misbehaves is worse than a program that doesn't work at all. Incorrect results can be worse than no results.
Failing early and hard eases debugging. This is the biggest reason. An end user of a program doesn't want the program to crash, but as a programmer I'm the end user of a library and I actually want it to crash. Crashing makes it evident that there's a bug I need to fix and the faster we hit the bug and the closer the crash is to the source of the bug, the faster and easier I can find it and fix it. A NULL pointer dereference is one of the most trivial bugs to catch, debug and fix. It's much easier than trawling through gigabytes of logs to spot one line that says "create_something had a null pointer".
With error returns, what if the caller catches that error, returns an error itself (in your example that would be err_create_something_failed) and its caller returns another error (err_caller_of_create_something_failed)? Then you have an error return 3 functions away, that might not even indicate what actually went wrong. And even if it manages to indicate what actually went wrong (by having a whole framework for error handling that records exactly where the error happened through the whole chain of callers) the only thing you can do with it is to look up the error value in some table and from that conclude that there was a NULL pointer in create_something. It's a lot of pain when instead you could just have opened a debugger and seen exactly where the assumption was violated and what exact chain of function calls lead to that problem.
In the same spirit you can use assert to validate other function arguments to cause early and easy to debug failures. Crash on the assert and you have the full correct call chain that leads to the problem. I just wouldn't use asserts to check pointers because it's pointless (at least on an operating system with memory management) and makes things slower while giving you the same behavior (minus the printed message).
You can use assert.h to check your parameters:
assert(pointer != NULL);
That will make the program fail on debug mode if 'ponter == NULL', but there will be no check at all on release so you can check everything you want with no performance hit.
Anyway if a function requires parameters within a range checking that is a waste of resources, it is the user of your API who should do the checks.
But is up to you how you want to design the API. There is no correct way on that matter: if a function expects a number between 1 and 5 and the user pass a 6 you can perform a check or simply specify that the function will have undefined behaviour.
There is no universally correct way to perform argument validation. In general, you should use assert when you can to validate arguments, but assert is usually disabled in non-debug builds and might not always be appropriate.
There are several things to consider that can vary from case to case, such as:
Do you expect your function to be called a lot? Is performance critical? If a caller will be invoking your function many, many times in a tight loop, then validating arguments can be expensive. This is especially bad for inline functions and if the runtime cost of the validation checks dwarfs the runtime cost of the rest of your function.
Are the checks easy for the caller to perform? If the checks are non-trivial, then it's less error-prone to do validation in the function itself than forcing the extra work on the callers. Note that in some cases, callers might not even be able to perform proper validation themselves (for example, if there's a possibility of a race condition in checking the argument's validity).
Is your function well documented? Does it clearly describe its preconditions, specifying what valid values for its arguments are? If so, then you usually should consider it the caller's responsibility to pass valid arguments.
Is your function self-documenting? Is it obvious to callers what valid arguments are?
Should passing a bad argument be a logic error or a runtime error? That is, should it be considered a programmer's mistake? Is it likely that the argument could come directly from user input? You should consider how you expect callers to use your function. If assertions are enabled, should a bad argument be fatal and terminate the program?
Who are your function's users? Is your function going to be used internally (where you might have some expectation for the competence of other programmers using it), or is it going to be exposed to the general public? If the latter, what failure mode will minimize the amount of technical support that you need to provide? (The stance I took with my dropt library is that I relied on assertions to validate arguments to internal functions and reported error codes for public functions.)
I notice that the standard c library contains several string functions that don't check the input parameter(whether it's NULL), like strcmp:
The string handle functions of the standard C library require "... pointer arguments on such a call shall still have valid values ..." C11dr §7.24.1 2
NULL is not a valid pointer to a string and there is no requirement on the function's part to check pointer validity, so no NULL check.
C's performance does come at a price.
When you design an API, would you check all parameters to make it function well or just let it go crash?
This enters design philosophy. Consider a simpler example. Should the API test the input parameters first? It depends on coding goals.
int add(int a, int b) {
return a + b;
}
// return 1 on failure
int add_safe(int *sum, int a, int b) {
if (a >= 0) {
if (b > INT_MAX - a) return 1; // Overflow
} else {
if (b < INT_MIN - a) return 1; // Underflow
} if (sum) { // NULL check
*sum = a + b;
}
return 0;
}
When in doubt, create an API that does nominal checking. Use strong checking of inputs that may originate from a user (human) or another process. If you want heavy pedantic checking, C is not an efficient target language choice.
With many an API I have made, NULL is a valid input value as a pointer and code adjusts its functionality base on that as above. A NULL check is made, but it is not an error check.

Is it a bad practice to output error messages in a function with one input and one output [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I was once told that functions with one input and one output(not exactly one) should not print messages when being called. But I don't understand. Is it for security or just for convention?
Let me give an example. How to deal with an attempt that access data in a sequential list with an incorrect index?
// 1. Give out the error message inside the function directly.
DataType GetData(seqList *L, int index)
{
if (index < 0 || index >= L->length) {
printf("Error: Access beyond bounds of list.\n");
// exit(EXIT_FAILURE);
}
return L->data[index];
}
// 2. Return a value or use a global variable(like errno) that
// indicates whether the function performs successfully.
StateType GetData(seqList *L, int index, int *data)
{
if (index < 0 || index >= L->length) {
return ERROR;
}
*data = L->data[index];
return OK;
}
I think there are two things going on here:
Any visible and unexpected side-effect such as writing to streams is generally bad, and not just for functions with one input and one output. If I was using a list library, and it started silently writing error messages to the same output stream I was using for my regular output, I'd consider that a problem. However, if you are writing such a function for your own personal use, and you know ahead of time that the action you want taken is always to print a message and exit(), then it's fine. Just don't force this behavior on everyone else.
This is a specific case of the general problem of how to inform callers about errors. A lot of the time, a function cannot know the correct response to an error, because it doesn't have the context that the caller does. Take malloc(), for instance. The vast majority of the time, when malloc() fails, I just want to terminate, but once in a great while I might want to deliberately fill the memory by calling malloc() until it fails, and then proceed to do something else. In this case, I don't want the function to decide whether or not to terminate - I just want it to tell me it's failed, and then pass control back to me.
There are a number of different approaches to handling errors in library functions:
Terminate - fine if you're writing a program yourself, but bad for a general purpose library function. In general, for a library function, you'll want to let the caller decide what to do in the case of an error, so the function's role is limited to informing the caller of the error.
Return an error value - sometimes OK, but sometimes there is no feasible error value. atoi() is a good case in point - all the possible values it returns could be correct translations of the input string. It doesn't matter what you return on error, be it 0, -1 or anything else, there is no way to distinguish an error from a valid result, which is precisely why you get undefined behavior if it encounters one. It's also semantically questionable from a slightly purist point of view - for instance, a function which returns the square root of a number is one thing, but a function which sometimes returns the square root of a number, but which sometimes returns an error code rather than a square root is another thing. You can lose the self-documenting simplicity of a function when return values serve two completely separate purposes.
Leave the program in an error state, such as setting errno. You still have the fundamental problem that if there is no feasible return value, the function still can't tell you that an error has occurred. You could set errno to 0 in advance and check it afterwards every time, but this is a lot of work, and may just not be feasible when you start involving concurrency.
Call an error handling function - this basically just passes the buck, since the error function then also has to address the issues above, but at least you could provide your own. Also, as R. notes in the comments below, other than in very simple cases like "always terminate on any error" it can be asking too much of a single global error handling function to be able to sensibly handle any error that might arise in a way that your program can them resume normal execution. Having numerous error handling functions and passing the appropriate ones individually to each library function is technically possible, but hardly an optimal solution. Using error handling functions in this way can also be difficult or even impossible to use correctly in the presence of concurrency.
Pass in an argument that gets modified by the function if it encounters an error. Technically feasible, but it's not really desirable to add an additional parameter for this purpose to every library function ever written.
Throw an exception - your language has to support them to do this, and they come along with all kinds of associated difficulties including unclear structure and program flow, more complex code, and the like. Some people - I'm not one of them - consider exceptions to be the moral equivalent of longjmp().
All the possible ways have their drawbacks and advantages, as of yet humanity has not discovered the perfect way of reporting errors from library functions.
In general you should make sure you have a consistent and coherent error handling strategy, which means considering whether you want to pass an error up to a higher level or handle it at the level it initially occurs. This decision has nothing to do with how many inputs and outputs a function has.
In a deeply embedded system where a memory allocation failure occurs at a critical juncture, for example, there's no point passing that error back up (and indeed you may well not be able to) - all you can do might be enter a tight loop to let the watchdog reset you. In this case there's no point reserving invalid return values to indicate error, or indeed in even checking the return value at all if it doesn't make sense to do so. (Note I am not advocating just lazily not bothering to check return values, that is a different matter entirely).
On the other hand, in a lovely beautiful GUI app you probably want to fail as gracefully as possible and pass the error up to a level where it can be either worked around / retried / whatever is appropriate; or presented to the user as an error if nothing else can be done.
It is better to use perror() to display error messages rather than using printf()
Syntax:
void perror(const char *s);
Also error messages are supposed to be sent to the stderr stream than stdout.
Yes, it's a bad practice; even worse is that you're sending the output to stdout rather than stderr. This could end up corrupting data by mixing error message in with output.
Personally, I believe very strongly that this kind of "error handling" is harmful. There is no way you can validate that the caller passed a valid value for L, so checking the validity of index is inconsistent. The documented interface contract for the function should simply be that L must be a valid pointer to an object of the correct type, and index a valid index (in whatever sense is meaningful to your code). If an invalid value for L or index is passed, this is a bug in your code, not a legitimate error that can occur at runtime. If you need help debugging it, the assert macro from assert.h is probably a good idea; it makes it easy to turn off the check when you no longer need it.
One possible exception to the above principle would be the case where the value of L is coming from other data structures in your program, but index is coming from some external input that's not under your control. You could then perform an external validation step before calling this function, but if you always need the validation, it makes sense to integrate it like you're doing. However, in that case you need to have a way to report the failure to the caller, rather than printing a useless and harmful message to stdout. So you need to either reserve one possible return value as an error sentinel, or have an extra argument that allows you to return both a result and an error status to the caller.
Return a reserved value that's invalid for a success condition. For example, NULL.
It is advisable not to print because:
It doesn't help the calling code reason about the error.
You're writing to a stream that might be used by higher level code for something else.
The error may be recoverable higher, so you might be just printing misleading error messages.
As others have said, consistency in how you deal with error conditions is also an important factor. But consider this:
If your code is used as a component of another application, one that does not follow your printing convention, then by printing you're not allowing the client code to remain faithful to its own strategy. Thus, using this strategy you impose your convention to all related code.
On the other hand, if you follow the "cleaner" solution of returning a reserved value and the client code wants to follow the printing convention, the client code can easily adapt to what you return and even print an error, by making simple wrappers around your functions. Thus, using this strategy you give the users of your code enough space to choose the strategy that best works for them and to be faithful to it.
It is always best if code deals with one thing only. It is easier to understand, it is easier to use, and is applicable in more instances.
Your GetData function that prints an error message isn't suitable for use in cases where there may not be a value. i.e. The calling code wants to try to get a value and handle the error by using a default if it doesn't exist.
Since GetData doesn't know the context it can't report a good error message. As an example higher up the call stack we can report hey you forgot to give this user an age, vs in GetData where all it knows is we couldn't get some value.
What about a multithreaded situation? GetData seems like it would be something that might get called from multiple threads. With a random bit of IO shoved in the middle it will cause contention over who has access to the console if all the threads need to write at the same time.

Check 'puts' return value, then what?

i'm starting to make code checking the function's return value, but i'm not sure on how to procede after I capture some errors.
For example, in fgets:
while( fgets( rta, 3, stdin ) == NULL ) {
printf( "Ocurrio un error al evaluar su respuesta. Intente nuevamente./n" );
}
But for puts ?
It will return EOF on error, so I make:
if( puts( "Message" ) == EOF ) {
error handle...
}
Question is, what I'm supossed to do if it fails. I would think in displaying a message in the console (this is a console app) but if puts fails, then my message would also fail.
(because I would also use puts).
Should I use assert to display a message and end the app?
Thanks a lot.
Unless you have some desperate need to communicate with someone/something that is no longer available, you typically ignore the return value of such functions.
If you really need a record of your progress or failure, then open a log file and write all of your messages there (or use something like syslog).
Basically, if you're using stdio, then it is likely someone at some point with either hook your program to a pipe (e.g. | less) or do some other such thing that will lead to stdout going away.
Programs like lint will complain when you ignore the return value of printf and puts, but the truth is 99% of those return values are useless except in extreme cases.
Well, as they say, If there's any doubt, there is no doubt.
It's very good of you to incorporate disciplined thinking like making sure all cases are accounted for.
But if you're unsure what to do with the return value, then clearly you don't need to do anything with it.
To communicate this decision in code, you may explicitly discard the return value by casting the expression to void.
(void)puts("");
This will hush-up any warning messages about ignoring the return value.

How to use sscanf correctly and safely

First of all, other questions about usage of sscanf do not answer my question because the common answer is to not use sscanf at all and use fgets or getch instead, which is impossible in my case.
The problem is my C professor wants me to use scanf in a program. It's a requirement.
However the program also must handle all the incorrect input.
The program must read an array of integers. It doesn't matter in what format the integers
for the array are supplied. To make the task easier, the program might first read the size of the array and then the integers each in a new line.
The program must handle the inputs like these (and report errors appropriately):
999999999999999...9 (numbers larger than integer)
12a3 (don't read this as an integer 12)
a...z (strings)
11 aa 22 33\n all in one line (this might be handled by discarding everything after 11)
inputs larger than the input array
There might be more incorrect cases, these are the only few I could think of.
If the erroneous input is supplied, the program must ask the user to input again until
the correct input is given, but the previous correct input must be kept (only incorrect
input must be cleared from the input stream).
Everything must conform to C99 standard.
The scanf family of function cannot be used safely, especially when dealing with integers. The first case you mentioned is particularly troublesome. The standard says this:
If this object does not have an appropriate type, or if the result of
the conversion cannot be represented in the object, the behavior is
undefined.
Plain and simple. You might think of %5d tricks and such but you'll find they're not reliable. Or maybe someone will think of errno. The scanf functions aren't required to set errno.
Follow this fun little page: they end up ditching scanf altogether.
So go back to your C professor and ask them: how exactly does C99 mandate that sscanf will report errors ?
Well, let sscanf accept all inputs as %s (i.e. strings) and then program analyze them
If you must use scanf to accept the input, I think you start with something a bit like the following.
int array[MAX];
int i, n;
scanf("%d", &n);
for (i = 0; i < n && !feof(stdin); i++) {
scanf("%d", &array[i]);
}
This will handle (more or less) the free-format input problem since scanf will automatically skip leading whitespace when matching a %d format.
The key observation for many of the rest of your concerns is that scanf tells you how many format codes it parsed successfully. So,
int matches = scanf("%d", &array[i]);
if (matches == 0) {
/* no integer in the input stream */
}
I think this handles directly concerns (3) and (4)
By itself, this doesn't quite handle the case of the input12a3. The first time through the loop, scanf would parse '12as an integer 12, leaving the remaininga3` for the next loop. You would get an error the next time round, though. Is that good enough for your professor's purposes?
For integers larger than maxint, eg, "999999.......999", I'm not sure what you can do with straight scanf.
For inputs larger than the input array, this isn't a scanf problem per se. You just need to count how many integers you've parsed so far.
If you're allowed to use sscanf to decode strings after they've been extracted from the input stream by something like scanf("%s") you could also do something like this:
while (...) {
scanf("%s", buf);
/* use strtol or sscanf if you really have to */
}
This works for any sequence of white-space separated words, and lets you separate scanning the input for words, and then seeing if those words look like numbers or not. And, if you have to, you can use scanf variants for each part.
The problem is my C professor wants me to use scanf in a program.
It's a requirement.
However the program also must handle all the incorrect input.
This is an old question, so the OP is not in that professor's class any more (and hopefully the professor is retired), but for the record, this is a fundamentally misguided and basically impossible requirement.
Experience has shown that when it comes to interactive user input, scanf is suitable only for quick-and-dirty situations when the input can be assumed to correct.
If you want to read an integer (or a floating-point number, or a simple string) quickly and easily, then scanf is a nice tool for the job. However, its ability to gracefully handle incorrect input is basically nonexistent.
If you want to read input robustly, reliably detecting incorrect input, and perhaps warning the user and asking them to try again, scanf is simply not the right tool for the job. It's like trying to drive screws with a hammer.
See this answer for some guidelines for using scanf safely in those quick-and-dirty situations. See this question for suggestions on how to do robust input using something other than scanf.
scanf("%s", string) into long int_string = strtol(string, &end_pointer, base:10)

Using scanf in a while loop

Probably an extremely simple answer to this extremely simple question:
I'm reading "C Primer Plus" by Pratta and he keeps using the example
while (scanf("%d", &num) == 1)...
Is the == 1 really necessary? It seems like one could just write:
while (scanf("%d", &num))
It seems like the equality test is unnecessary since scanf returns the number of objects read and 1 would make the while loop true. Is the reason to make sure that the number of elements read is exactly 1 or is this totally superfluous?
In C, 0 is evaluated to false and everything else to true. Thus, if scanf returned EOF, which is a negative value, the loop would evaluate to true, which is not what you'd want.
Since scanf returns the value EOF (which is -1) on end of file, the loop as written is correct. It runs as long as the input contains text that matches %d, and stops either at the first non-match or end of file.
It would have been clearer at a glance if scanf were expecting more than one input....
while (scanf("%d %d", &x, &y)==2) { ... }
would exit the loop when the first time it was unable to match two values, either due to end of file end of file (scanf returns EOF (which is -1)) or on input matching error (e.g. the input xyzzy 42 does not match %d %d so scanf stops on the first failure and returns 0 without writing to either x or y) when it returns some value less than 2.
Of course, scanf is not your friend when parsing real input from normal humans. There are many pitfalls in its handling of error cases.
Edit: Corrected an error: scanf returns EOF on end of file, or a non-negative integer counting the number of variables it successfully set.
The key point is that since any non-zero value is TRUE in C, failing to test the return value correctly in a loop like this can easily lead to unexpected behavior. In particular, while(scanf(...)) is an infinite loop unless it encounters input text that cannot be converted according to its format.
And I cannot emphasize strongly enough that scanf is not your friend. A combination of fgets and sscanf might be enough for some simple parsing, but even then it is easily overwhelmed by edge cases and errors.
You understood the C code correctly.
Sometimes the reason for testing the number of items read is that someone wants to make sure that all items were read instead of scanf quitting early when it the input didn't match the expected type. In this particular case it didn't matter.
Usually scanf is a poor choice of functions because it doesn't meet the needs of interactive input from a human user. Usually a combination of fgets and sscanf yield better results. In this particular case it didn't matter.
If later chapters explain why some kinds of coding practices are better than this trivial example, good. But if not, you should dump the book you're reading.
On the other hand, your substitute code isn't exactly a substitute. If scanf returns -1 then your while loop will execute.
While you are correct it is not strictly necessary, some people prefer it for several reasons.
First, by comparing to 1 it becomes an explicit boolean value (true or false). Without the comparison, you are testing on an integer, which is valid in C, but not in later languages (like C#).
Secondly, some people would read the second version in terms of while([function]), instead of while([return value]), and be momentarily confused by testing a function, when what is clearly meant is testing the return value.
This can be completely a matter of personal preference, and as far as I'm concerned, both are valid.
One probably could write it without an explicit comparison (see the JRL's answer though), but why would one? I'd say that comparison-less conditions should only be used with values that have explicitly boolean semantics (like an isdigit() call, for example). Everything else should use an explicit comparison. In this case (the result of scanf) the semantics is pronouncedly non-boolean, so the explicit comparison is in order.
Also, the comparison one can usually omit is normally a comparison with zero. When you feel the urge to omit the comparison with something else (like 1 in this case) it is better to think twice and make sure you know what your are doing (see the JRL's answer again).
In any case, when the comparison can be safely omitted and you actually omit it, the actual semantical meaning of the condition remains the same. It has absolutely no impact on the efficiency of the resultant code, if that's something you are worrying about.

Resources