Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Don't we have to assign return values of the functions to variables? C/C++

I've been using C/C++ for about three years and I can't believe I've never encountered this issue before!

This following code compiles (I've just tried using gcc):

#include <iostream>

int change_i(int i) {
  int j = 8;
  return j;
}

int main() {
  int i = 10;
  change_i(10);
  std::cout << "i = " << i << std::endl;
}

And, the program prints i = 10, as you might expect.

My question is -- why does this compile? I would have expected an error, or at least a warning, saying there was a value returned which is unused.

Naively, I would consider this a similar case to when you accidentally forget the return call in a non-void function. I understand it's different and I can see why there's nothing inherently wrong with this code, but it seems dangerous. I've just spotted a similar error in some very old code of mine, representing a bug which goes back a long time. I obviously meant to do:

i = change_i(10);

But forgot, so it was never changed (I know this example is silly, the exact code is much more complicated). Any thoughts would be much appreciated!

like image 522
Mike Avatar asked Aug 12 '16 13:08

Mike


4 Answers

It compiles because calling a function and ignoring the return result is very common. In fact, the last line of main does so too.

    std::cout << "i = " << i << std::endl;

is actually short for:

    (std::cout).operator<<("i =").operator<<(i).operator<<(std::endl);

... and you are not using the value returned from the final operator<<.

Some static checkers have options to warn when function returns are ignored (and then options to annotate a function whose returns are often ignored). Gcc has an option to mark a function as requiring the return value be used (__attribute__((warn_unused_result))) - but it only works if the return type doesn't have a destructor :-(.

like image 97
Martin Bonner supports Monica Avatar answered Nov 20 '22 19:11

Martin Bonner supports Monica


Ignoring the return value of a function is perfectly valid. Take this for example:

printf("hello\n");

We're ignoring the return value of printf here, which returns the number of characters printed. In most cases, you don't care how many characters are printed. If compilers warned about this, everyone's code would show tons of warnings.

This actually a specific case of ignoring the value of an expression, where in this case the value of the expression is the return value of a function.

Similarly, if you do this:

i++;

You have an expression whose value is discarded (i.e. the value of i before being incremented), however the ++ operator still increments the variable.

An assignment is also an expression:

i = j = k;

Here, you have two assignment expressions. One is j = k, whose value is the value of k (which was just assigned to j). This value is then used as the right hand side an another assignment to i. The value of the i = (j = k) expression is then discarded.

This is very different from not returning a value from a non-void function. In that case, the value returned by the function is undefined, and attempting to use that value results in undefined behavior.

There is nothing undefined about ignoring the value of an expression.

like image 41
dbush Avatar answered Nov 20 '22 21:11

dbush


The short reason it is allowed is because that's what the standard specifies.

The statement

 change_i(10);

discards the value returned by change_i().

The longer reason is that most expressions both have an effect and produce a result. So

i = change_i(10);

will set i to be 8, but the assignment expression itself also has a result of 8. This is why (if j is of type int)

j = i = change_i(10);

will cause both j and i to have the value of 8. This sort of logic can continue indefinitely - which is why expressions can be chained, such as k = i = j = 10. So - from a language perspective - it does not make sense to require that a value returned by a function is assigned to a variable.

If you want to explicitly discard the result of a function call, it is possible to do

(void)change_i(10);

and a statement like

j = (void)change_i(10);

will not compile, typically due to a mismatch of types (an int cannot be assigned the value of something of type void).

All that said, several compilers (and static code analysers) can actually be configured to give a warning if the caller does not use a value returned by a function. Such warnings are turned off by default - so it is necessary to compile with appropriate settings (e.g. command line options).

like image 39
Peter Avatar answered Nov 20 '22 21:11

Peter


I've been using C/C++ for about three years

I can suppose that during these three years you used standard C function printf. For example

#include <stdio.h>

int main( void )
{
    printf( "Hello World!\n" );
}

The function has return type that differs from void. However I am sure that in most cases you did not use the return value of the function.:)

If to require that the compiler would issue an error when the return value of a function is not used then the code similar to the shown above would not compile because the compiler does not have an access to the source code of the function and can not determine whether the function has a side effect.:)

Consider another standard C functions - string functions.

For example function strcpy is declared like

char * strcpy( char *destination, const char *source );

If you have for example the following character arrays

char source[] = "Hello World!";
char destination[sizeof( source )];

then the function usually is called like

strcpy( destination, source );

There is no sense to use its return value when you need just to copy a string. Moreover for the shown example you even may not write

destination = strcpy( destination, source );

The compiler will issue an error.

So as you can see there is sense to ignore sometimes return values of functions.

For your own example the compiler could issue a message that the function does not have a side effect so its call is obsolete. In any case it should issue a message that the function parameter is not used.:)

Take into account that sometimes the compiler does not see a function definition that is present in some other compilation unit or in a library. So the compiler is unable to determine whether a function has a side effect,

In most cases compilers deal with function declarations. Sometimes the function definitions are not available for compilers in C and C++.

like image 28
Vlad from Moscow Avatar answered Nov 20 '22 19:11

Vlad from Moscow