-1

I was working on a function where I wanted to pass a parameter as a pointer and assign its value inside the function. I got to a solution with using a pointer to a pointer and using a pointer alone. It made scratch my head on when would a pointer to a pointer be required if a pointer can act exactly the same when assigning a pointer's value from a parameter?

View my sample code below.

// pointers.c
void foo(int *f) {
  *f = 1;
}

void bar(int **f) {
  int y = 2;
  int *temp = &y;
  (*f) = temp;
}

void baz(int *f) {
  int y = 3;
  int *temp = &y;
  f = temp;
}

int main(int argc, char const *argv[]) {
  int j = 0;
  int *num = &j;
  printf("%d\n", *num);
  foo(num);
  printf("%d\n", *num);
  bar(&num);
  printf("%d\n", *num);
  baz(num);
  printf("%d\n", *num);
  return 0;
}

$ gcc pointers.c -o pointers -Wall && ./pointers 
0
1
2
3

The accepted answer in Why use double pointer? or Why use pointers to pointers?, does not comeback with why in C a double pointer would be required to use.

lostAtSeaJoshua
  • 1,695
  • 2
  • 21
  • 34
  • 2
    Your code causes undefined behaviour, `bar` sets `num` to point to an int which is destroyed – M.M Mar 03 '19 at 22:59
  • There's only one case where a pointer to a pointer is *required*, and you've got it in your code. `char *argv[]` is just another way of expressing `char **argv` (when used as a parameter), and that's the only way to take command line arguments. (There are, of course, many other cases in which a pointer to a pointer is *useful*.) – Ray Mar 04 '19 at 00:44

3 Answers3

4

This is all sorts of broken. Your program does not indicate that baz() works as you want it to. It only seems to because of other brokenness.

The variable y in bar() is local to it. It and its storage are only valid during the execution of bar(). After bar() completes, y is no longer valid and neither is its storage. And yet, you've made num point to that storage. That will result in undefined behavior. It's just happenstance that the storage was not reused or overwritten and continues to hold 2 at the time you print its value.

baz() does not affect what num points to. f in that function is a parameter and thus local to baz(). The assignment to f only affects that local.

So, why does printing *num after calling baz() produce "3"? Because num still points to the storage of y from the call to bar() and the call to baz() overwrote that storage with 3. Again, that's happenstance and can't be relied upon.

You really do need to use a pointer to pointer to affect num, as you did in bar(). But you must not use pointers to locals after you've returned from their scope.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Okay so it looks like my code has many flaws in it. Thanks for pointing those out, but back to the question when do I need a double pointer? I have seen methods such as strcpy assign a parameter’s value using just a pointer. Is that the correct way? – lostAtSeaJoshua Mar 03 '19 at 23:27
  • Also based on your answer, what about the method foo? Is their anything wrong with it? Does that actually assign num correctly? – lostAtSeaJoshua Mar 03 '19 at 23:28
  • 1
    `foo()` assigns through `f` (which is the same pointer as `num`) to `j`. It does not change `num` itself. Likewise, `strcpy()` writes through the destination pointer to the character array it points to, but does not change the destination pointer itself. A function's parameters are always local copies of whatever the caller passed. The function can change their value, but that only changes the local copy. – Ken Thomases Mar 04 '19 at 02:06
  • 1
    If you want a function to change a variable of the caller, you need to pass a pointer to that variable and write through the pointer. If the caller's variable is itself a pointer, then you need a pointer to a pointer. – Ken Thomases Mar 04 '19 at 02:07
1

there are multiple uses of double, triple, ... pointers in 'c'. Extrapolating your example, the double pointer can be used to assign a value to a pointer passed to a function as a parameter.

So, if you take your bar function, there is some part of the usage in it:

void bar(int **f) {
   ...
   (*f) = temp;
}

int main () {
    int *ptr;
    bar(&ptr);
 }

in the above example the function bar would assign a value to the pointer ptr, which can later be used in the main.

Though, your problem is that in your example you assign to it a pointer which is only valid inside of the function. When function returns, ptr would be assigned a value, as it existed inside of the function, but it will point to an invalid value. Behavior of the program will not be defined at this point.

This behavior can be used to return a pointer to a dynamically allocated memory or to a static variable, i.e.

void bar(int **f) {
    int *tmp = (int*)malloc(sizeof(int));
    *tmp = 10;
    *f = tmp;
}

In this example tmp will be assigned a pointer for a dynamically allocated memory, which will persist till one used free. You can assign value of tmp to *f and use it later int he main. There are also uses for static or global variables, which persist after the return from the function.

Serge
  • 11,616
  • 3
  • 18
  • 28
0

After searching in StackOverflow I have found that a pointer to a pointer is required as a function parameter when you want to change what a pointer is pointing to in a function.

I found the following links Edit where a pointer points within a function and Changing address contained by pointer using function

C has no pass by reference so it works as below:

A pointer is simply a normal variable that holds the address of something else as its value. If you pass an initialized pointer to a function, the function receives a copy of the pointer holding the original address as its value. You can change the value at the memory address held by the pointer by dereferencing it and assigning a new value to that memory address. Any other pointer that points to that memory address also points to the new value. This works for the value pointed to by the pointer, but you cannot change the address of the pointer itself (remember it is only a copy of the original pointer with its own and very different address that is local to the function, any change to the address of the pointer itself will be lost on function return)

To change the pointer itself within a function (such as when you realloc, etc..) you must pass the address of the pointer as a parameter. E.g. foo (&the_pointer) The function parameter must be a pointer-to-pointer (e.g. a double pointer). It follows the same semantics, but now you are passing the address of the pointer by value. You are then free to change the address of the pointer by assigning the new pointer address to the dereferenced pointer within the function. The change will then be visible back in the calling function.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
lostAtSeaJoshua
  • 1,695
  • 2
  • 21
  • 34