0

I am trying to write a function that receives a pointer and makes it point to a new object. In order to do this, I am using a ptr-to-ptr. This is what my function looks like:

void modifyPtr(int ** ptrToPtr)
{
    *ptrToPtr = new int(42);
    return;
}

I would like my function to work even when ptrToPtr is a nullptr:

int ** ptrToPtr = nullptr;
modifyPtr(ptrToPtr);

My first implementation of the function is unsafe for this, because it would dereference a nullptr. So I thought of the following:

void modifyPtr(int ** ptrToPtr)
{
    ptrToPtr = &(new int(42));
    return;
}

But taking the address of the pointer returned by the new operator is not allowed, producing the following error during compilation:

error: lvalue required as unary '&' operand

So I thought of one last option:

void modifyPtr(int ** ptrToPtr)
{
    int * temp = new int(42);
    ptrToPtr = &temp;
    return;
}

But this produces a segmentation fault during runtime, which I expected because as soon as the temp pointer goes out of scope, it is deleted, resulting in a memory leak.

Is there a way to make my function work with a nullptr?

user3266738
  • 477
  • 2
  • 12
  • 2
    Try `new int*(new int(42))`. You're trying to get a pointer to a literal when you do &(new int(42)) – pepperjack Jan 10 '18 at 03:10
  • 3
    You should learn instead about smart pointers ([std::unique_ptr](http://en.cppreference.com/w/cpp/memory/unique_ptr)/[std::shared_ptr](http://en.cppreference.com/w/cpp/memory/shared_ptr)). You already got the solution [here](https://stackoverflow.com/questions/48178943/validating-a-pointer-to-a-pointer-in-c) to pass by reference. – O'Neil Jan 10 '18 at 03:30
  • Thanks for the suggestion @O'Neil! I posted this question because I was interested in learning an alternative to the one provided in my other question. And thanks for your answer @pepperjack, it works perfectly! – user3266738 Jan 10 '18 at 03:52
  • References exist entirely for this reason. Double pointers are so C. – Alex Huszagh Jan 10 '18 at 04:51

3 Answers3

2

In your example, you're passing int ** ptrToPtr by value to your modifyPtr() function. That means you can't modify the value itself, as it's actually a copy of the value provided to the function on the stack.

A proper way to do this (i.e., alternative to other methods provided in the comments) is:

int* ptr = nullptr;
modifyPtr(&ptr);

Passing the address of the int* (i.e., the pointer to the pointer) allows you to modify it and assign it a value.

Phil Brubaker
  • 1,257
  • 3
  • 11
  • 14
2

You need to validate the actual pointer passed into the function before you can dereference it to access the variable being pointed to, eg:

void modifyPtr(int ** ptrToPtr)
{
    if (ptrToPtr)
        *ptrToPtr = new int(42);
}

int** ptr = nullptr;
modifyPtr(ptr); // <- ptr will not be dereferenced!

int* ptr = nullptr;
modifyPtr(&ptr); // <- OK
...
delete ptr; // <- don't forget this!

Depending on the nature of the function, it may or may not need to free the memory being pointed to before changing the pointer to point at new memory, eg:

void initPtr(int ** ptrToPtr)
{
    if (ptrToPtr)
        *ptrToPtr = new int(42);
}

void modifyPtr(int ** ptrToPtr)
{
    if (ptrToPtr)
    {
        delete *ptrToPtr;
        *ptrToPtr = new int(45);
    }   
}

int* ptr;
initPtr(&ptr);
...
modifyPtr(&ptr);
...
delete ptr;

So be aware of that.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
2

To make a function that modifies a pointed object work with a null pointer, I assume that you want to create a new object. This is a bad idea: When the input is non-null, it does not create a new pointer object. Doing something completely different with different inputs is a bad design and will easily confuse the reader of the code.

Instead, I suggest that you don't attempt to make the function "work" with null pointer input but instead require that an address of a pointer is passed, rather than null). This can be enforced at compile time by using a reference argument instead of a pointer to pointer to prevent the caller from making a mistake:

void modifyPtr(int*& ptr)
{
    ptr = new int(42);
}
// Usage
int* ptr;
modifyPtr(ptr);     // OK
modifyPtr(nullptr); // does not compile; as was intended

Or, maybe you intend to always create a new pointer as you do in your broken example, in which case the argument is completely pointless. You could simply return a pointer:

int* createObject()
{
    return new int(42);
}
// Usage
int* ptr = createObject();

Ignoring the improved design that I suggested, you can make the function create a new pointer and return its address. It's simply not possible using automatic storage. You could use dynamic storage instead. But even then, you would have to pass the pointer to pointer by reference in order to actually modify it:

void modifyPtr(int**& ptrToPtr)
{
    if(!ptrToPtr)
        ptrToPtr = new int*;
    *ptrToPtr = new int(42);
}

However, you must remember that being dynamically allocated, both the pointer to pointer, and the pointer to object must be deleted manually. This design has no advantages to what I suggested earlier.


But this produces a segmentation fault during runtime, which I expected because as soon as the temp pointer goes out of scope, it is deleted, resulting in a memory leak.

The memory is indeed leaked, but a memory leak does not cause a segmentation fault. Your suggested function only modifies local copy of the pointer to pointer, so whatever variable was passed to the function would remain unmodified. If that value is indeed null, then subsequent dereferencing of the pointer would have undefined behaviour. Resulting in a segmentation fault if you're lucky.


Finally, allocating dynamic memory outside of a container is a bad idea in general. You should use a smart pointer instead. The final version that I actually recommend would be:

std::unique_ptr<int> createObject()
{
    return new int(42);
}

Of course, this particular example is so trivial that writing a function for it makes no sense in the first place.

eerorika
  • 232,697
  • 12
  • 197
  • 326