开发者

Function returns value without return statement

开发者 https://www.devze.com 2023-02-03 14:48 出处:网络
Why does fol开发者_Python百科lowing code has a correct output? int GGT has no return statement, but the code does work anyway? There are no global variables set.

Why does fol开发者_Python百科lowing code has a correct output? int GGT has no return statement, but the code does work anyway? There are no global variables set.

#include <stdio.h>
#include <stdlib.h>

int GGT(int, int);

void main() {
    int x1, x2;
    printf("Bitte geben Sie zwei Zahlen ein: \n");
    scanf("%d", &x1);
    scanf("%d", &x2);
    printf("GGT ist: %d\n", GGT(x1, x2));
    system("Pause");
}

int GGT(int x1, int x2) {
    while(x1 != x2) {
        if(x1 > x2) {
            /*return*/ x1 = x1 - x2;
        }
        else {
            /*return*/ x2 = x2 - x1;
        }
    }
}


For x86 at least, the return value of this function should be in eax register. Anything that was there will be considered to be the return value by the caller.

Because eax is used as return register, it is often used as "scratch" register by callee, because it does not need to be preserved. This means that it's very possible that it will be used as any of local variables. Because both of them are equal at the end, it's more probable that the correct value will be left in eax.


It should not work and certainly do not work on all compilers and target OS, even if it works on yours.

The likely explanation is that a function returning int always return something, and it's usually the content of a register. It probably happens that the register used for return value is in your case the same used to compute the last expression before returning from the function (on x86 targets, certainly eax).

This being said, an optimizing compiler detecting that there is no return is allowed to completely remove the code of this function. Henceforth the effect you see (may) disappear when activating higher optimizations levels.

I tested it with gcc:

gcc without optimization: inputs 10, 20 -> result is 10

gcc -O1 inputs 10, 20 -> result is 1

gcc -O2 inputs 10, 20 -> result is 0


If a function is defined to return a value but does not, and the calling function attempts to use the return value, you invoke undefined behavior.

This is spelled out in section 6.9.1p12 of the C standard:

If the } that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined.

You got "lucky" in this case that the program appeared to work properly, but there's no guarantee of that. Had you compiled with different optimization settings or a different compiler altogether you could end up with different results.

So the moral of the story: if the function says it returns a value, always return a value.


On x86 the return value is stored in EAX register, which "accidentally" is also used by this compiler to store the result of arithmetic operations (or at least subtraction). You can check this by looking at assembly generated by your compiler. I agree with kriss - you can't assume this will always be the case, so it's better to explicitly specify the return value.


Official verbiage:

6.9.1 Function definitions
...
12 If the } that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined.

where "undefined benavior" means:

1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.

C 2011 Online Draft

Believe it or not, a function typed something other than void is not required to have a return statement. It's not enforced by the language grammar, and there is no constraint that a non-void function must contain a return statement. The only constraints are that, if it is present, a return statement in a void function not return the value of any expression, and that a return statement in a non-void function must return the value of an expression.

Why is that the case?

C initially did not have a void data type, and there was no way to specify a subroutine that was only executed for its side effects and didn't return a value. There was no good way to return "nothing", so the return statement was not required. C also at this time had "implicit int" declarations - you could define a function body without a type, and the compiler would assume it was typed int:

foo( a, b )   // old style parameter declarations
  int a;      // still legal, but no longer really used
  char *b;    // for very good reasons
{
  // do something interesting with a and b
}

foo is implicitly typed to return int, even if no value is explicitly being returned. This is okay as long as the caller doesn't try to use foo's non-existent return value. So a convention sort of developed where "procedures" (functions executed solely for side effects) were not explicitly typed, and had no return statement.

Because legacy code is forever, the behavior with respect to return statements hasn't been changed in more recent versions of the C standard, even though implicit int declarations were removed in C99.


GCC pastes "ret" instruction in such case, while clang pastes "ud2" and app crashes at run-time.

0

精彩评论

暂无评论...
验证码 换一张
取 消