tags:

views:

357

answers:

4

I have the following data structures:

struct Inner
{
    int myValue;
};

struct Outer
{
    Inner* inner;
};

Option 1

If I do the following:

Outer outer;
outer.inner = NULL;

outer.inner = new inner;
inner* pInner = outer.inner;

and then add a watch for the following 2 values:

  • outer.inner
  • pInner

then they are both non-NULL, and equal in value.

Option 2

If I do the following:

Outer outer;
outer.inner = NULL;

inner* pInner = outer.inner;
if (! pInner)
{
    pInner = new inner;
}

then pInner points to valid heap memory, and outer.inner is still NULL.

Questions

  1. Why are the options different - shouldn't they both be pointing at the same memory in option 2 as well?

  2. Assuming that I went with option 1, and I was then reading values out of inner. I could then use pInner and outer.inner interchangeably couldn't I? Why is this different to allocating the memory?

+1  A: 

In C++, any assignment a = b (or copy constructor <<sometypehere>> a = b) normally copies bits from b to a for plain old datatypes (you can override operator= and copy constructors to get different effects, but that's obviously not the case here since the types are pointers;-). So, if you modify b later, that has absolutely no effect on a, which just keeps the very same exact bits the assignment or copy constructor copied to it.

Re 2, as long as the two pointers are exactly the same bits (you assigned either one from the other and didn't change either of them after assigning) then of course you can indifferently use either for access (be that writing or reading through it). Assignment (and copying) is what is entirely and enormously different (the fact that the assigment or copy is of a pointer to freshly allocated memory is a red herring).

Alex Martelli
+1  A: 
Outer outer;
outer.inner = NULL;

Inner* pInner = outer.inner;
if (! pInner)
{
    pInner = new Inner;
}

Why did you expect outer.inner to change after assigning new Inner to pInner? They are independent.

You need to make pInner a reference to outer.inner:

Inner*& pInner = outer.inner;

Then option 2 works like you intended.


P.S. identifiers should not differ by case only, which is error-prone. case in point: "new inner" and "inner* pInner"

Ozan
+6  A: 

A pointer stores a memory address. In your first option you store the memory location of the object that gets allocated by new inner in outer.inner. Then you copy that value to pInner and both variables contain the same address.

In the second option first outer.inner is set to contain the address NULL. The this value is copied to pInner and both variables contain NULL. After that pInner is changed to contain the address of a newly allocated object, but the address stored in outer.inner is not affected by this. That address still is NULL.

Pointers are just numbers that happen to be memory addresses. The different assignments work the same as if the variables were declared as int. That these numbers are in fact memory addresses only becomes interesting when * or -> is used to dereference the pointer. Two different pointer variables might point to the same address, but they are still different variables.

sth
A: 

Suppose instead of pointers, we are talking about ints. Pointers are just containers whose values are memory addresses, which are simply numbers anyway. Consider the following code,

struct Inner {
  int myValue;
}

struct Outer {
  int inner;
}

Outer outer;
outer.inner = 0;

int pInner = outer.inner;

if(!pInner) {
  Inner inner;
  pInner = &inner;
}

In this code, you would not expect pInner to have the same value as outer.inner would you?


Perhaps belaboring the point: a pointer is just a value, like any int. Let's deconstruct your example, given this knowledge:

Outer outer;
outer.inner = NULL;

Right here, outer.inner == 0 is true. You assigned 0 to outer.inner.

inner* pInner = outer.inner;

Now, you copied the value 0 to pInner, so pInner == 0 also. Note that it is only a memory address, not the data at that address. In this case the memory address is 0x00000000.

if (! pInner)

Evaluates to true because pInner == 0.

{
    pInner = new Inner;
}

Now, pInner is assigned a new integer generated by the system. pInner no longer has the same integer value as inner.outer.

Jesse Dhillon