Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modify the location a pointer points to in a C++ function

Tags:

c++

pointers

I have gotten stuck on modifying pointers of pointers. The problem is that I don't understand why my code works. What I am trying to do is modify where a pointer-to-pointer points to in a function. Then accessing that value in my main function. I tried quite a few attempts and this is the only way I got it to work.

#include <iostream>
using namespace std;
void changePP(int **ppint) {
    int *n = new int;
    *n = 9; //just a value for demonstration purposes
    *ppint = n; //THE LINE IN QUESTION
    delete n;
}
int main() {
    int **ppint = NULL;
    int *p = new int;
    *p = 4; //another value for demonstrating
    ppint = &p;
    cout << **ppint << endl;
    changePP(ppint);
    cout << **ppint << endl;
}

So, the output is 4 and then a 9 on seperate lines. However, I am not sure about the line *ppint = n in the code. Why do I have to use the * to change where ppint points to in the changePP function but not in the main? Also why do I not have to use the & in the function? I can't seem to find an explanation that I can understand on the internet and I was wondering if someone could explain this for me?

like image 936
Anthony Dito Avatar asked Mar 07 '15 02:03

Anthony Dito


3 Answers

Note: these memory addresses are only for illustration.

Memory Layout

0x0A | 4
0x0B | 'p' -> 0x0A // Pointer to the value 4 in location 0x0A
0x0C | 'ppint' -> NULL // Initially a null pointer

Performing ppint = &p; produces the following because here ppint is the pointer located at 0x0C.

0x0A | 4
0x0B | 'p' -> 0x0A
0x0C | 'ppint' -> 0x0B

As for the parameter in the changePP function, I will refer to it as ppintCopy for less confusion. It's a copy (i.e., a pointer different than ppint) and modifying it only modifies the copy.

0x0D | 'ppintCopy' -> 0x0C // A pointer that points to `ppint`

Performing ppintCopy = &n would modify the pointer at 0x0D which is not the location of the pointer from the main function. However, deferencing ppintCopy (i.e., *ppintCopy) yields 0X0C which is the pointer from the main function therefore *ppintCopy = n; is changing where the pointer 0x0C points.

Why do I have to use the * to change where ppint points to in the changePP function but not in the main?

With the above illustration I hope it is more clear why it works in main and why you have to use a different syntax in the changePP function.

Also why do I not have to use the & in the function?

In the main function the variable ppint is of type int** (i.e., a pointer to a pointer) and p is of type int*. You cannot perform ppint = p; because the they are different types. This may be easier to see if you remove one level of indirection. For example:

int* pMyInt = NULL;
int myInt = 3;

I think it's pretty self explanatory that this cannot work (i.e., they are different types).

pMyInt = myInt;

However you can take the address of myInt using the & operator which results in a pointer to int (i.e., int*) and since this type is the same as the type of pMyInt the assignment is now possible.

pMyInt = &myInt;
like image 192
James Adkison Avatar answered Nov 15 '22 04:11

James Adkison


ppint; // the variable name
*ppint; // dereference the first layer of pointer
*(*ppint);  // dereference the first layer of pointer which points to another pointer that contains a **value**

Thus *ppint = n; means point ppint to another memory location which is the location assigned to n or it could be rewritten as *ppint = &n;

After that line, you have deleted the n but you didn't change its value so the value remains unchanged unless something have accessed and modified it.

The reason why you still retain the value of n after deleting it is considered as undefined behaviour though.

like image 20
mr5 Avatar answered Nov 15 '22 02:11

mr5


Pointers are already messy to deal with, you didn't have to make it worse. This works as well:

#include <iostream>
using namespace std;
void changePP(int *ppint) {
    *ppint = 9;
}
int main() {
    int p = 4;
    int *ppint = &p;
    cout << *ppint << endl; // this could be replaced with /*p*/
    changePP(ppint); // note you could have replaced this parameter with /*&p*/
    cout << *ppint << endl; // this could be replaced with /*p*/
}

Let me try to explain what your code is doing, but please for future, don't ever do it this way. Do it the way I've shown

#include <iostream>
using namespace std;
void changePP(int **ppint) {
    int *n = new int; // you allocate memory for a single integer (again it is uninitialised)
    *n = 9; // the value at the above pointer is now changed to 9
    *ppint = n; // here you have just dereferenced a pointer to pointer which gives you a pointer, so in essence you are changing the location in memory where this pointer is pointing and it is now pointing to whatever memory address n was
    delete n;
}
int main() {
    int **ppint = NULL; // Here you have declared a pointer to pointer which is NULL
    int *p = new int; // You can do better here with /*new int(4)*/
    *p = 4; // this changes the value that p initially held (which could be anything)
    ppint = &p; // reference to a pointer always creates a pointer to a pointer and since that is what ppint is, you are safe here
    cout << **ppint << endl; // print the value nested within this pointer
    changePP(ppint); // now you call the function with a variable of type pointer to pointer. Note you could have passed &p as parameter here and it would have still worked
    cout << **ppint << endl;
}

What is the moral of the story?

When passing a parameter v to a function,

  • &v will produce a pointer to v
  • *v will dereference v and will give you access to whatever v was pointing to

When v is a parameter in a function,

  • &v will bring the actual object v into the scope of the function. This is called pass by reference
  • *v Is a pointer to v, and this is still pass by reference called pass by value because you are passing a pointer(Thanks Kirk)
like image 25
smac89 Avatar answered Nov 15 '22 02:11

smac89