1

Here is the function definition

const int& test_const_ref(const int& a) {
  return a;
}

and calling it from main

int main() {
  auto& x = test_const_ref(1);
  printf("%d, %p\n", x, &x);
}

output as following

 ./debug/main
>>> 1, 0x7ffee237285c

and here is the disassembly code of test_const_ref

test_const_ref(int const&):
    pushq  %rbp
    movq   %rsp, %rbp
    movq   %rdi, -0x8(%rbp)
    movq   -0x8(%rbp), %rax
    popq   %rbp
    retq

The question is: where does the variable x alias or where is the number 1 I passed to function test_const_ref stored ?

Colin Wang
  • 23
  • 4

3 Answers3

3

The code exhibits undefined behavior - the function test_const_ref returns a reference to a temporary, which lives until the end of the full-expression (the ;), and any dereference of it afterwards accesses a dangling reference.

Appearing to work is a common manifestation of UB. The program is still wrong. With optimization on, for example, Clang 12 -O2 prints: 0.

Note - there's no error in the function test_const_ref itself (apart from a design error). The UB is in main, where the dereference of the dangling int& happens during a call to printf.

Where the temporary int is stored exactly is implementation detail - but in many cases (in a Debug build, when a function isn't inlined), it would be stored on the stack:

main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     dword ptr [rbp - 12], 1      # Here the 1 is stored in the stack frame
        lea     rdi, [rbp - 12]
        call    test_const_ref(int const&)
        mov     qword ptr [rbp - 8], rax
        mov     rax, qword ptr [rbp - 8]
        mov     esi, dword ptr [rax]
        mov     rdx, qword ptr [rbp - 8]
        movabs  rdi, offset .L.str
        mov     al, 0
        call    printf

So any subsequent use of the returned reference will access memory at [rbp - 12], that may already have been re-used for other purposes.


Note also that the compiler doesn't actually generate assembly from C++ code; it merely uses the C++ code to understand the intent, and generates another program that produces the intended output. This is known as the as-if rule. In the presence of undefined behavior, the compiler becomes free from this restriction, and may generate any output, rendering the program meaningless.

rustyx
  • 80,671
  • 25
  • 200
  • 267
  • That's make sense to me and help me a lot. Thank you very much. Since I haven't got enough reputation, so sorry that I can't vote you. : ) – Colin Wang Jun 07 '21 at 03:28
1

Good answers are already been given, but wrapperm explained this topic very well in here. It's going to be stored on the stack in most implementations i'm aware of.

sodaluv
  • 449
  • 2
  • 9
  • 22
0

1. The function

The language doesn't define where arguments to functions are stored. Different ABIs, for different platforms, define this.

Typically, a function argument, before any optimization, is stored on the stack. A reference is no different in this respect. What's actually stored would be a pointer to the refered-to object. Think of it this way:

const int* test_const_ref(const int* a) {
  return a;
}

2. The temporary

If you were to declare a variable int foo; and call test_const_ref(foo), you know that the result would refer to foo. Since you're calling it with a temporary, all bets are off: As @fabian notes in a comment, the language only guarantees the value exist until the end of the assignment statement. Afterwards

In practice, and in your case: A compiler which allocates stack space for the temporary integer 1 will have x refer to that place, and will not use it for something else before x is defined. But if your compiler optimizes that stack allocation away - e.g. passes 1 via a register - then x has nothing to refer to and may hold junk. It might even be undefined behavior (not quite sure about that). If you're lucky, you'll get a compiler warning about it (GodBolt.org).

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • Sorry for missing the complier I used. I try the program with Clang, everything went well, but got warning with gcc – Colin Wang Jun 06 '21 at 09:43