4

I know that "literals" (c strings, int or whatever) are stored somewhere (in a read only data section apparently .rodata) maybe this is not accurate...

I want to understand why this code causes a runtime error:

#include <iostream>
using namespace std;

const int& foo()
{
    return 2;
}
const char* bar()
{
    return "Hello !";
}

int main() {

    cout << foo() << endl; // crash

    cout << bar() << endl; // OK

    return 0;
}

foo returns a const reference on a literal (2) why does this cause a crash ? is the integer 2 stored in the stack of foo() ?

See also : Why are string literals l-value while all other literals are r-value?

Community
  • 1
  • 1
Aminos
  • 754
  • 1
  • 20
  • 40
  • 8
    Integer literals are usually stored in the code as instructions like `MOV eax, 0xdeadf00d`, and their only active lifetime is in a register. – Ryan Bemrose Oct 14 '16 at 17:10
  • 4
    `return 2` will create a temp variable, which will be free after `return`. `return "hello"` will return the address of literal("hello") – Yves Oct 14 '16 at 17:18
  • @Thomas but 2 isn't a literal ? it's like having int tmp = 2; return tmp; ???? – Aminos Oct 14 '16 at 17:26
  • 1
    @Aminos when we `int tmp = 2`, it means: give me a variable, whose value is 2. when we ` const char * p = "hello"`, it means: give a variable, whose value is the address of "hello". So "hello" is always there even we free `p`. – Yves Oct 14 '16 at 17:28
  • 1
    Possible duplicate of [Why can't you take the address of nullptr?](http://stackoverflow.com/questions/14189709/why-cant-you-take-the-address-of-nullptr) – Ryan Bemrose Oct 14 '16 at 17:34
  • `tmp` is a variable (instead of literal), which contains a copy of `2`. `p` is a variable, which contains a copy of the address of "hello". – Yves Oct 14 '16 at 17:43

2 Answers2

5

I see why this is confusing so I will try to break it down.

First case:

const int& foo()
{
    return 2;
}

The return statement makes a temporary object which is a copy of the literal 2. So its address is either non-extant or different from the location of the literal 2 (assuming literal 2 has a location - not guaranteed).

It is that temporary whose reference is returned.

Second case:

const char* bar()
{
    return "Hello !";
}

The return statement makes a temporary object which is a copy of a pointer to the address of the first element of the literal char array. That pointer contains the actual address of the literal array and that address is returned by copy to the caller.

So to sum up. The second one works because the return statement takes a copy of the literal's address and not a copy of the literal itself. It doesn't matter that the storage for the address is temporary because the address still points to the correct place after the temporary holding its value collapses.

Galik
  • 47,303
  • 4
  • 80
  • 117
  • the copy of "2" exists in which namespace ? foo, main or it ceases to exist after return 2; (the semicolon) – Aminos Oct 14 '16 at 18:48
  • 1
    @Aminos namespaces don't really apply to temporaries. It may exist only in a `CPU` register. It's up to the compiler where it stores it. This is why it is illegal to take the address of a temporary. It ceases to exist immediately after the function hands the "return value" back to the caller. In this case the "return value" is actually a reference. – Galik Oct 14 '16 at 19:45
3

That is indeed very confusing, and in order to understand what's happening, one has to dive very deep in the language specification.

But before we do this, let me remind you that compiler warnings are your friends. With a sufficient level of warnings, you should see following when compiling your example:

In function 'const int& foo()': 3 : warning: returning reference to temporary [-Wreturn-local-addr] return 2; ^

Now, what is happening in your first example? One can not really take an address of the integral literal, since they do not really exist as objects. However, one is allowed to bind constant references to literals. How is it possible, when everybody knows that references are akin to pointers? The reason is that when you bind a const reference to the literal, you do not really bind it to the literal. Instead, compiler creates a temporary variable, and binds your reference to it. And that variable is an object, albeit short-lived one. Once you function returns, the temporary object is destroyed, and you end up with dangling reference -> crash.

In the second example, "hello" is a literal, but you are not returning the literal - you are returning a pointer to the string. And a pointer remains valid, because the string it points to remains valid.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • the copy of "2" exists in which namespace ? foo, main or it ceases to exist after return 2; (the semicolon) ? – Aminos Oct 14 '16 at 18:49