Function With Missing Return Value, Behavior At Runtime

Function with missing return value, behavior at runtime

It is Undefined behaviour as specified in the ISO C++ standard section 6.6.3:

Flowing off the end of a function is
equivalent to a return with no value;
this results in undefined behavior in
a value-returning function.

When a function missing the return value, the compiler generates a warning but not an error?

You can use -Werror=return-type to make that warning and error, in my original comment I forgot that. You can see it live.

This is both an option in clang and gcc, as far as I understand XCode can use either one.

Falling off the end of value returning function is undefined behavior, we can see this by going to the draft C++ standard section 6.6.3 The return statement paragraph 2 which says:

[...]Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.

Undefined Behavior does not require a diagnostic(warning or error), although in many cases compilers will provide one.

Missing return statement in C++ can still work

Your code has undefined behavior, so anything could happen, including doing what you intended. Under Clang, you don't get the result you expected.

I made minimal modifications to get your code to compile and exercise the path with undefined behavior:

struct Node { int key; Node *lchild; Node *rchild; };
Node *search(Node *head, int x){
if(head == nullptr) return nullptr;
else if(x == head->key) return head;
else if(x <= head->key) search(head->lchild, x);
else search(head->rchild, x);
}
Node a { 0, nullptr, nullptr };
Node b { 1, &a, nullptr };
int main() { search(&b, 0); }

Clang warns on your code by default, and causes your code to crash when it falls off the end of the function at -O0:

$ clang++ -std=c++11 wszdwp.cpp
wszdwp.cpp:7:1: warning: control may reach end of non-void function [-Wreturn-type]
}
^
1 warning generated.
$ ./a.out
zsh: illegal hardware instruction (core dumped) ./a.out

With Clang's -fsanitize=undefined, I get this:

$ clang++ -std=c++11 -w -fsanitize=undefined wszdwp.cpp && ./a.out
wszdwp.cpp:2:7: runtime error: execution reached the end of a value-returning function without returning a value

The code was probably working for you because your compiler "helpfully" put in a ret instruction at the end of the body of the function (without filling in the return value reigster, so the return value is likely to be inherited from the previous function you called).

C++ output shown without returning the values

Write this code:

#include<iostream>
using namespace std;

int add(int x,int y)
{
return x+y;//CHANGE THIS
}

int main()
{
int a,b;
cin>>a>>b;
int d=add(a,b);
cout<<d;
}

What does the standard say when there is no return?

(Question was originally (mis-)tagged as C too).

The behaviour of your program is undefined in both C and C++.

The only int function that has an implicit return value is main, and 0 is implied in that case; again in both C and C++.

Is it Undefined behavior to not having a return statement for a non-void function in which control can never off over the end?

The two statements are in no way contradictory.

The first statement is about what happens when control flow exits a non-void function without executing a return statement. The second statement is about what happens when control flow does not exit the function at all. Calls to functions like exit or std::terminate do not ever have control flow proceed past the point when those functions are called.

But that has nothing to do with the nature of the return value.

The behavior of the program when a non-void function runs out of stuff to do without an explicit return statement (or throw. Or co_return these days) is governed by [stmt.return]/2:

Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.

Why and how does GCC compile a function with a missing return statement?

What happened for you is that when the C program was compiled into assembly language, your toUpper function ended up like this, perhaps:

_toUpper:
LFB4:
pushq %rbp
LCFI3:
movq %rsp, %rbp
LCFI4:
movb %dil, -4(%rbp)
cmpb $96, -4(%rbp)
jle L8
cmpb $122, -4(%rbp)
jg L8
movzbl -4(%rbp), %eax
subl $32, %eax
movb %al, -4(%rbp)
L8:
leave
ret

The subtraction of 32 was carried out in the %eax register. And in the x86 calling convention, that is the register in which the return value is expected to be! So... you got lucky.

But please pay attention to the warnings. They are there for a reason!

Erratic behaviour with missing return in c++ and optimizations

You may want to check out this answer here

The just of it is that the compiler allows you to not have a return statement since there are potentially many different execution paths, ensuring each will exit with a return can be tricky at compile time, so the compiler will take care of it for you.

Things to remember:

if main ends without a return it will always return 0.

if another function ends without a return it will always return the last value in the eax register, usually the last statement

optimization changes the code on the assembly level. This is why you are getting the weird behavior, the compiler is "fixing" your code for you changing when things are executed giving a different last value, and thus return value.

Hope this helped!

No return value from function with if statment

If a function has a return type, it MUST return something. So, you can either:

Return a sentinel value that the function and caller agree on to represent a "non-value", eg:

int div(int n1, int n2)
{
if (n2 != 0)
{
return n1 / n2;
}
else
{
return -1; // or whatever makes sense for your use-case...
}
}

If there is no sentinel value available that you can use, then you can use std::optional instead (C++17 and later only), eg:

#include <optional>

std::optional<int> div(int n1, int n2)
{
if (n2 != 0)
{
return n1 / n2;
}
else
{
return std::nullopt;
}
}

Or, you can change the function to use an output parameter and a bool return value, eg:

bool div(int n1, int n2, int &result)
{
if (n2 != 0)
{
result = n1 / n2;
return true;
}
else
{
return false;
}
}

Otherwise, you will just have to throw an exception instead, eg:

#include <stdexcept>

int div(int n1, int n2)
{
if (n2 == 0)
{
throw std::invalid_argument("n2 must not be 0");
}
return n1 / n2;
}


Related Topics



Leave a reply



Submit