0

Consider the following C++ code:

#include <iostream>
using namespace std;

class Hello {
    public:
    Hello(const char* name) : name(name)
    {}

    void body() {
        cout << name;
        cout << " body\n";
    }
    const char* name;
};


Hello* first;

void create() {
    Hello wellington("Wellington");
    first = &wellington;
}

int main() {
    create();
    Hello rob("Rob");
    Hello james("James");
    Hello donald("Donald");    

//    cout << "First points to: " << first << "\n";
//    cout << "Rob is at: " << &rob << "\n";
//    cout << "Donald is at: " << &donald << "\n";

    first->body();
}

Clearly, in the function create, we create a Hello object "Wellington", which's pointer we save in first and which ends up being destructed after the end of this procedure. Afterwards, a few more Hello objects are created, "Donald" being the last one.

The surprising part: despite "first" pointing to a "dead" object, the body method is correctly executed.. as if first was pointing to Donald. However, the pointers are different, the Donald pointer coming after the point first points to and Rob is located at. Another strange thing is that the name again fails to be printed correctly when the commented-out lines (printing the pointers) are included. Finally, checking the name of the pointed to object using first->name only returned gibberish, as would be expected.

So, what's going on here? Why is the body method correctly executed with Donald's name, despite the pointed-to object no longer existing?

  • 5
    undefined behavior. – tkausl May 10 '20 at 20:07
  • 1
    Does this answer your question? [Can a local variable's memory be accessed outside its scope?](https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope) – Asteroids With Wings May 10 '20 at 20:49
  • This _is_ it failing due to pointing to a dead object. – Asteroids With Wings May 10 '20 at 20:55
  • 1
    What's likely happening here is that the stack space that holds `wellington` is unchanged from when the object is constructed until `main` calls another function and overwriting that data. You code appears to work because the inline functions do not create a call. If the `Hello` constructor was not inline you'd probably have a different result. – 1201ProgramAlarm May 10 '20 at 21:06

2 Answers2

1

What is written is a code producing a so-called undefined behavior (UB) in part where it is dereferencing a pointer to an object which lifetime has ended.

it just so happened that your dead object address, captured by "first" matched the one of the existing objects.

Anything is possible when the code is UB, which is why it must not be written.

  • What exactly do you mean by "your dead object address, captured by 'first' matched the one of the existing objects"? As stated above, the address pointed at by first is different than that of any of the other created objects (at least according to the output through cout). – StckXchnge-nub12 May 10 '20 at 20:25
  • Dead object is the object which lifetime has ended, the one created by create() function. – Alexey Polyudov May 11 '20 at 00:51
  • Why I said that address of the dead object is the same, despite your experimental evidence? Because your other experimental evidence says that is it the same: it behaves as if it's the same. Anyway, it's UB. nothing to discuss here – Alexey Polyudov May 11 '20 at 01:03
0

The surprising part: despite "first" pointing to a "dead" object, the body method is correctly executed..

Why is this surprising to you? Accessing through an invalid pointer has undefined behaviour. The behaviour is not guaranteed to be something other than "correctly executed" (for whatever is your expectation of "correct") when it is undefined.

Another strange thing is that the name again fails to be printed correctly when the commented-out lines (printing the pointers) are included.

Why is this strange to you? The behaviour is not guaranteed to be "printed correctly again" (for whatever is your expectation of "correct") when it is undefined.

Nothing about the behaviour of the program is guaranteed when it is undefined.

So, what's going on here? Why is the body method correctly executed with Donald's name, despite the pointed-to object no longer existing?

The behaviour of the program is undefined.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • So in other words, it's "sheer luck" that the program's memory is, at that moment, in such a state that "correct execution" occurs? I'd say it is surprising to me because I did find an apparent pattern here (creating new Objects causes the name/body of the last object to be printed, defining integers/primitives beforehand didn't seem to change the results). So I reckon there must be something that causes this regularity, probably regarding how the compiler handles memory management. – StckXchnge-nub12 May 10 '20 at 20:20
  • @StckXchnge-nub12 You cannot trust patterns when undefined behaviour is involved. `probably regarding how the compiler handles memory management` Perhaps. Feel free to read the source code of your compiler to find out. Note that your findings might not necessarily hold for other compilers, nor different versions of the same compiler. – eerorika May 10 '20 at 20:23
  • @StckXchnge-nub12, I suggest you run a debugger on the generated assembly code and see what actually happens there. – Evg May 10 '20 at 20:24