4

I thought I was fairly good with C++, it turns out that I'm not. A previous question I asked: C++ const lvalue references had the following code in one of the answers:


#include <iostream>
using namespace std;

int& GenX(bool reset) { static int* x = new int; *x = 100; if (reset) { delete x; x = new int; *x = 200; } return *x; }

class YStore { public: YStore(int& x); int& getX() { return my_x; } private: int& my_x; };

YStore::YStore(int& x) : my_x(x) { }

int main() { YStore Y(GenX(false)); cout << "X: " << Y.getX() << endl; GenX(true); // side-effect in Y cout << "X: " << Y.getX() << endl; return 0; }

The above code outputs X: 100, X:200. I do not understand why. I played with it a bit, and added some more output, namely, a cout before the delete x; and a cout after the new x; within the reset control block.

What I got was: before delete: 0x92ee018 after new: 0x92ee018

So, I figured that static was silently failing the update to x, and the second getX was playing with (after the delete) uninitialized memory; To test this, I added a x = 0; after the delete, before the new, and another cout to ensure that x was indeed reset to 0. It was.

So, what is going on here? How come the new returns the exact same block of memory that the previous delete supposedly free'd? Is this just because that's what the OS's memory manager decided to do, or is there something special about static that I'm missing?

Thank you!

Community
  • 1
  • 1
please delete me
  • 711
  • 2
  • 9
  • 17
  • While your question ended up boiling down to something other than static, there are [4 different meanings of static](http://stackoverflow.com/q/1995513). –  Nov 11 '10 at 09:11

5 Answers5

9

That's just what the memory manager decided to do. If you think about it, it makes a lot of sense: You just freed an int, then you ask for an int again... why shouldn't the memory manager give you back the int you just freed?

More technically, what is probably happening when you delete is that the memory manager is appending the memory block you freed to the beginning of the free list. Then when you call new, the memory manager goes scanning through its free list and finds a suitably sized block at the very first entry.

For more information about dynamic memory allocation, see "Inside storage allocation".

Martin B
  • 23,670
  • 6
  • 53
  • 72
  • True. I have tried adding `int * MyPerturbation = new int;` just after the `delete x;`, and `delete MyPerturbation;` just after `x = new int;`: the output is different. – Wok Nov 11 '10 at 08:45
  • @Martin B: The "why shouldn't.." is quite obvious in hindsight. I was focusing on the static keyword for some odd reason. Your answer makes perfect sense. – please delete me Nov 11 '10 at 08:45
  • @wok: Good test. I should've done that prior to posting and saved everyone some trouble :). Thanks! – please delete me Nov 11 '10 at 08:48
  • His answer is not correct. That is UB, and anything can happen (including nasal demons). – BЈовић Nov 11 '10 at 08:48
  • @VJo: The question was not if the access was UB, the question was why is the address of the block the same both before the delete, and after the new. I think Martin B answered that perfectly. I hasten to add, that obviously, you're right, and the above is UB. The question's intent wasn't to find that out, though. – please delete me Nov 11 '10 at 08:54
  • @VJo: This actually isn't UB *as long as the allocator returns the same piece of memory.* You're allowed to destruct an object, reconstruct an object of identical type in its place, then continue to use "old" references/pointers. Is it fragile? Would it be UB if the allocator behaved differently? For both: hell, yes. –  Nov 11 '10 at 09:08
1

You are accessing the memory block that is deallocated. By the c++ standard, that is an undefined behaviour, therefore anything can happen.

EDIT

I guess I have to draw :

  1. You allocate the memory for an int, and you pass the object allocated on the heap to the constructor of Y, which stores that in the reference
  2. then you deallocate that memory, but your object Y still holds the reference to the deallocated object
  3. then you access the Y object again, which holds an invalid reference, referencing deallocated object, and the result you got is the result of an undefined behaviour.

EDIT2

The answer to why : implementation defined. The compiler can create new object at any location it likes.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • 3
    Technically, you are not. The new returns the same old memory block that delete destructed. but, yes, this could obviously change and is implementation specific. I was just curious about the "why" here. Thanks for your response! – please delete me Nov 11 '10 at 08:49
1

To your first question:

X: 100, X:200. I do not understand why.

Since Y.my_x is just a reference to the static *x in GenX, this is exactly how it supposed it to be - both are referencing to the same address in memory, and when you change the content of *x, you get a side effect.

Doc Brown
  • 19,739
  • 7
  • 52
  • 88
  • 1
    The question was really *why* doesn't the address change, even after the new/delete. But, I probably didn't make that clear enough. – please delete me Nov 11 '10 at 08:53
  • There is an "edit" button below your post to improve your question any time afterwards. And there is "code" symbol above the editor to help you to get your code samples correctly formatted ;-) – Doc Brown Nov 11 '10 at 08:59
  • This answer isn't quite right or, at least, it tells me something different than what happens. Y.my_x is a reference to the object `*x` at a particular time, but it's only an implementation quirk that x gets the same value again in order to construct an object at the right place for Y.my_x to still be valid. –  Nov 11 '10 at 13:02
1

i test your code in VC2008, the output is X: 100, X: -17221323. I think the reason is that the static x is freed. i think my test is reasonable.

Du Hui
  • 11
  • 1
  • Your test is reasonable, the results the OP got are extremely dependent on his implementation. –  Nov 11 '10 at 13:00
-1

This makes perfect sense for the code.

Remember that it is your pointer that is static, so when you enter this function a second time you do not need to make a new pointer, but ever time you enter this function you are making a new int for the pointer to point to.

You are also probably in debug mode where a bit more time is spent giving you nice addresses.

Exactly why the int that your pointer points to is in the same space is probably just down to pure luck, that and you are not declaring any other variables before it so the same space in memory is still free

thecoshman
  • 8,394
  • 8
  • 55
  • 77