В чем разница между этими двумя способами объявления указателя на указатель? - PullRequest
0 голосов
/ 20 октября 2011

Посмотрите на этот фрагмент кода:

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);

выход

$ ./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

Так какой путь (возможно) более предпочтителен в этих двух объявлениях указателя на указатель, и есть ли какое-либо техническое различие между этими двумя?

Ответы [ 4 ]

4 голосов
/ 20 октября 2011

Технически, вы объявили только один указатель на указатель.

int **ppi = π // first declaration

Второе объявление - это просто указатель, который при назначении получает копию первого указателя pi.адрес.

Вот как вы можете это доказать;после кода теста добавьте следующее:

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);

Вывод будет:

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

При изменении значения pi (на что оно указывает) разыменование ppi будет отражать изменения, так как он указывает на pi, но поскольку api был просто сделан как копия pi до его изменения, он будет продолжать указывать на i.

0 голосов
/ 20 октября 2011

Типы определяют семантику того, как вы взаимодействуете с данными, вам никогда не следует назначать указатель на int напрямую, например, на указатель на указатель на int.Однако вам может быть интересно использовать вариант использования типа **.

Например, посмотрите на этот код, который я написал:

#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;  
}

Вывод на моем компьютере:

[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

Существуют функции и стандартная библиотека, и в других местах, которые принимают указатель на указатель типа.Пока параметр ** не является константным, он указывает, что значение может быть изменено, и может указывать или не указывать на одну и ту же выделенную память.В моем примере я удалил старый указатель, но это специфическая функция, за которой нужно следить.Кроме того, если вызывающая сторона ответственна за удаление любой новой возвращенной памяти, она также может зависеть от конкретной функции, поэтому прочитайте документацию по любым функциям, которые, как вы видите, их используют, и убедитесь, что вы документируете все, что пишете хорошо.

0 голосов
/ 20 октября 2011

Ваши последние два printf s (ppi и api) пропускают свой второй аргумент, поэтому он печатает все, что окажется в стеке, где должен быть указанный аргумент (компиляция с соответствующими предупреждениями могла бы это уловить) , api НЕ является указателем на указатель, это указатель на int, такой же как pi. ЕДИНСТВЕННЫЙ способ объявить указатель на указатель (игнорируя интервалы и стили в скобках) - type **var.

0 голосов
/ 20 октября 2011

Второй (api) не объявляет указатель на указатель. Он создает указатель на int и инициализирует его значением другого указателя на int.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...