In C++, there are generally two kinds of memory that you use.
- Heap memory - This is what you use when you call
- Stack memory - This is what you use when you declare a non-static local variable inside a function.
The heap lasts for the entire lifetime of your program. You can access it from anywhere in the program if you know the memory location of your data. Heap memory needs to be allocated using some OS syscall, e.g.
brk() in Linux. Once you are done with the memory, you must release it. This can be done by
delete() or most commonly automatically when an allocated object’s destructor is run.
The stack memory is local to the function where you declare a variable. It can be used by callees but not the callers. So you can use this memory as long as the current frame is not popped out of the stack.
Let us look at this program:
func() returns the memory address of
sum which is a stack variable. The problem is that on returning from
func(), the frame is popped out of the stack, which includes the local variables declared in
a now points to a memory that is no longer supposed to be used.
So what happens if you compile it? Well, you get a warning from the compiler.
But we’re using C++ and we know exactly what we’re doing, so let us ignore this warning and execute the program.
Huh… that’s strange. Let’s find out why that happened.
This is extremely peculiar. The address returned from
func() and stored into
a is 0.
Let us rewrite the program and make use of the powerful debugging technique known as
Let’s run this.
The address of
0x7ffe48dbc344, but for some reason the value returned to
main() is 0.
At this point I was totally stumped, so I tried compiling the two variants of the program with
clang instead of
First thing to note is that the original program ran as we expected. It returned the address to
sum and printed the value stored at that address, even though that frame had been popped out.
Secondly, the revised program with
cout shows that the address returned into
a is the same as address of
sum, but for some reason the value stored there has changed from the expected
Let’s ignore the change in value for now, since accessing a memory location from a popped out frame is probably undefined behavior and the compiler is free to do whatever it likes when you go into undefined territory.
Let’s try to find out why
clang give us different programs for the same code. What if we looked at the disassembly? I will use the super useful Compiler Explorer for this purpose.
GCC 10.1 (x86-64) produces the following for
Towards the end, note how
0 is moved into
eax then the function returns. The register
eax is generally used to return values. So GCC is explicitly returning 0, instead of the address we wanted.
Clang 10.0.0 (x86-64) produces the following for
The generated code is almost exactly the same, except that Clang returns the address we wanted. This is why this program did the expected thing and printed the sum.
Why is this happening? Why this difference between GCC and Clang? I don’t know. Since we are doing something that is undefined in the C++ standard, the compiler is free to do whatever it wants. If I ever figure out why GCC is explicitly sabotaging our return value, I’ll make a new post about that!