tags:

views:

302

answers:

9

I know this has a really simple explanation, but I've been spoiled by not having to use pointers for a while.

Why can't I do something like this in c++

int* b;
foo(b);

and initialize the array through...

void Something::foo(int* a)
{
  a = new int[4];
}

after calling foo(b), b is still null. why is this?

+6  A: 

You pass parameter to foo by value. Change to

foo(int*& a)
Tadeusz Kopec
+4  A: 

You were passing-by-value.

You need to pass-by-reference:

void Something::foo(int* &a)
{
  a = new int[4];
}

Maybe it is clearer to do this:

int* Something::foo()
{
  int* a = new int[4];
  return a;
}

int* b = foo();

Make sure that when you are returning a pointer (or updating a reference) to memory allocated by a method, that it is clear when and by whom it should be freed.

Karl Voigtland
+12  A: 

Pointers are passed to the function by value. Essentially, this means that the pointer value (but not the pointee!) is copied. What you modify is only the copy of the original pointer.

There are two solutions, both using an additional layer of indirection:

  1. Either you use a reference:

    void f(int*& a) {
        a = new int[4];
    }
    
  2. Or you pass in a pointer-to-pointer (less conventional for out parameters as in your case):

    void f(int** pa) {
       *pa = new int[4];
    }
    

    And call the function like this:

    f(&a);
    

Personally, I dislike both styles: parameters are for function input, output should be handled by the return value. So far, I've yet to see a compelling reason to deviate from this rule in C++. So, my advise is: use the following code instead.

int* create_array() {
    return new int[4];
}
Konrad Rudolph
Why would you ever write 'f(int** pa)' in C++ (Maybe in C). Now you need to check for NULL. Can you re-order so that the safer technique is at the top (and add the NULL check).
Martin York
@Martin: `NULL` check doesn't help at all since what you'd really need in C++ is a pointer validity check – which doesn't exist. So no, the code is complete though brittle, it's the caller's responsibility to ensure the pointer is correct and I agree that this isn't code you'd want to write. *However*, there are actually people who see benefit in this style because it makes it obvious *on the caller's site* that the function modifies the argument's value. This isn't obvious with the reference. Personally, I disagree – but then, I never use `out` parameters, ever.
Konrad Rudolph
+2  A: 

For the same reason you cannot initialize an integer by passing it by value.

Sinan Ünür
A: 
void Something::foo(int **p)
 {

  *p=new int[4];
 }
adatapost
+1  A: 

Two points to make here:

1)

int* b;

this should really be:

int* b = NULL;

It's only reporting in the debugger that it's null because you're running under debug. Compiled under release will give b a random pointer.

2)

If you pass a pointer to the pointer, then you function should be able to allocate memory:

int* b = NULL;
Something::foo(&b);

void Something::foo(int** a)
{
    *a = new int[4];
}
Alan
A: 

You must either use a reference or do something like:

int* b;
foo(b);

void Something::foo(int** a)
{
  *a = new int[4];
}

(This is the old-school C way of doing it, I think a reference is better)

Ken Keenan
A: 

Because you are passing NULL to the function. What you need is something like (pointers to pointers):

int* b;
foo(b);

void Something::foo(int** a)
{
  *a = new int[4];
}
DanDan
+1  A: 

A pointer is an integer value (32-bit OS is a 32-bit integer) that IS the memory address. You pass the integer value into the function. The pass happens by pushing the value on to the stack. You then remove the value from the stack and change the value by allocating some memory over it. You then exit the function and the stack frame is deleted. Any values in that stack frame are removed, including your newly allocated memory address.

What you want to do is pass in a pointer to a pointer.

int* b;
foo(&b);

and

void Something::foo(int** a)
{
  *a = new int[4];
}

Here you declare b. It is an integer value pointing to a memory location. You pass in the address of that integer value. ie ANOTHER integer value that points to the memory location that holds an integer value that points to the memory location. Now inside the foo function you allocate memory to the address that you are pointing to that points to another memory location. Then when you exit the function the stack unwinds and the pointer to a pointer disappears. However your original integer value (ie pointer) still holds the value that you just created with your call to new.

Goz
+1 for good low-level explanation.
Terje Mikal