简体   繁体   中英

What is the difference between these 2 ways of declaring a pointer to pointer?

Have a look at this code snippet:

int i = 10;
int *pi = &i;
int **ppi = π // first declaration
int *api = pi;   // second declaration

printf("i's value is: %d\n",i);
printf("pi's value is: %d\n",*pi);
printf("ppi's value is: %d, at the address: %d\n",**ppi);
printf("api's value is: %d, at the address: %d\n",*api);

Output

$ ./test
i's value is: 10
pi's value is: 10
ppi's value is: 10, at the address: 2686760
api's value is: 10, at the address: 2686760

So which way is (perhaps) more preferrable in those 2 declarations of pointer to pointer, and is there any technical difference between those two?

Technically, you've only declared one pointer-to-pointer.

int **ppi = π // first declaration

The second declaration is just a pointer which, when assigned, gets a copy of the first pointer pi 's address.

Here's how you can prove it; after your test code, add this:

int i2 = 20;
pi = &i2;

printf("ppi's value is: %d, at the address: %d\n",**ppi,*ppi);
printf("api's value is: %d, at the address: %d\n",*api,api);

The output will be:

ppi's value is: 20, at the address: 2686764 (or some other address)
api's value is: 10, at the address: 2686760

When you change the value of pi (what it points to), dereferencing ppi will reflect the changes, as it points to pi , but because api was just made as a copy of pi before it changed, it will continue pointing to i .

The second one ( api ) doesn't declare a pointer to pointer. It creates a pointer to int, and initializes it with the value of another pointer to int.

Your last two printf s ( ppi and api ) are missing their second argument, so it's printing whatever happens to be in the stack where said argument should be (compiling with appropriate warnings would have caught this). api is NOT a pointer to a pointer, it's a pointer to an int , same as pi . The ONLY way to declare a pointer to a pointer (ignoring spacing and parentheses styles) is type **var .

The types defined the semantics of how you interact the data, you should not ever assign a pointer to int directly to a pointer to a pointer of an int, for example. However you might be wondering a use case of using a ** type.

For an example, look at this code I wrote:

#include <stdio.h>
#include <stdlib.h>

typedef struct fooStruct 
{
  int a;
  int b;
} FOO_STRUCT;

void multiReturns(int *a, FOO_STRUCT **b) {
  *a = 42;
  //b is a pointer that points to a FOO_STRUCT pointer
  if(*b) {
    free(*b);
    printf("Proof that we deleted memory.\n");
  }
  //This changes what b points to in main.
  *b = (FOO_STRUCT *)malloc(sizeof(FOO_STRUCT));
  printf("The new value of *b, this may or maynot be the same as before: %p\n", *b);
  //Parens for clarity, We dereferenced the pointer to the 
  //FOO_STRUCT pointer. Then, we derefrence the FOO_STRUCT
  //pointer to access its members.
  (*(*b)).a = 59;
  (*(*b)).b = 42;
  return;
}

int main(int argc, char **argv)
{
  int a = 0;
  FOO_STRUCT *b = (FOO_STRUCT *)malloc(sizeof(FOO_STRUCT));
  (*b).a = 1;
  (*b).b = 2;
  printf("BEFORE a = %d\n", a);
  printf("BEFORE b = %p\n", b);
  printf("BEFORE *b.a = %d\n", (*b).a);
  printf("BEFORE *b.b = %d\n", (*b).b);
  multiReturns(&a, &b);
  printf("AFTER a = %d\n", a);
  //The value AFTER b might have changed, depends on allocation factors..
  printf("AFTER b = %p\n", b);
  printf("AFTER *b.a = %d\n", (*b).a);
  printf("AFTER *b.b = %d\n", (*b).b);
  free(b);
  return 0;  
}

The output on my machine:

[hart@katamari tests]$ gcc pointers.c -o ptr
[hart@katamari tests]$ ./ptr
BEFORE a = 0
BEFORE b = 0x19b41010
BEFORE *b.a = 1
BEFORE *b.b = 2
Proof that we deleted memory.
The new value of *b, this may or maynot be the same as before: 0x19b41010
AFTER a = 42
AFTER b = 0x19b41010
AFTER *b.a = 59
AFTER *b.b = 42

There are functions and the standard library, and elsewhere, that take a pointer to a pointer of a type. As long as a parameter of ** is not const qualified, it indicates that the value can be changed and it may, or maynot point to the same allocated memory. In my example I deleted the old pointer, but that is function specific you need to watch out for. Also, if the caller is responsible to delete any new memory returned could also be function specific, so read the documentation of any functions you see that use these and make sure you document any that you write well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM