В каком случае указатель на указатель требуется в C? - PullRequest
0 голосов
/ 04 марта 2019

Я работал над функцией, в которой я хотел передать параметр в качестве указателя и назначить его значение внутри функции.Я пришел к решению с помощью указателя на указатель и с помощью одного указателя.Это заставило меня почесать голову, когда потребуется указатель на указатель, если указатель может действовать точно так же при назначении значения указателя из параметра?

Просмотреть мой пример кода ниже.

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

Принятый ответ в Зачем использовать двойной указатель?или Зачем использовать указатели на указатели? , не возвращается с тем, почему в C потребуется двойной указатель для использования.

Ответы [ 3 ]

0 голосов
/ 04 марта 2019

После поиска в StackOverflow я обнаружил, что указатель на указатель необходим в качестве параметра функции, когда вы хотите изменить то, на что указывает указатель в функции.

Я нашел следующие ссылки Изменить, где указатель указывает внутри функции и Изменение адреса, содержащегося в указателе, с использованием функции

C имеетнет передачи по ссылке, поэтому он работает следующим образом:

Указатель - это просто обычная переменная, которая содержит адрес чего-то еще в качестве значения.Если вы передаете инициализированный указатель на функцию, функция получает копию указателя, содержащую в качестве значения исходный адрес.Вы можете изменить значение по адресу памяти, удерживаемому указателем, разыменовав его и присвоив этому значению новое значение.Любой другой указатель, который указывает на этот адрес памяти, также указывает на новое значение.Это работает для значения, на которое указывает указатель, но вы не можете изменить адрес самого указателя (помните, что это всего лишь копия исходного указателя с собственным и очень другим адресом, который является локальным для функции, любое изменениеадрес самого указателя будет потерян при возврате функции)

Чтобы изменить сам указатель внутри функции (например, когда вы realloc и т. д.), вы должны передать адрес указатель в качестве параметра.Например, foo (&the_pointer) Параметр функции должен быть указатель-указатель (например, двойной указатель).Он следует той же семантике, но теперь вы передаете адрес указатель по значению.После этого вы можете изменить адрес указателя, назначив новый адрес указателя разыменованному указателю в функции.Затем изменение будет видно в вызывающей функции.

0 голосов
/ 04 марта 2019

есть многократное использование двойных, тройных, ... указателей в 'c'.Экстраполируя ваш пример, двойной указатель можно использовать для присвоения значения указателю, передаваемому функции в качестве параметра.

Итак, если вы возьмете свою функцию bar, некоторая часть использования вit:

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

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

в вышеприведенном примере функция bar присвоит значение указателю ptr, который позже может быть использован в main.

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

Это поведение можно использовать для возврата указателя на динамически распределенную память или статическую переменную, т. Е.

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

В этомВ примере tmp будет назначен указатель для динамически выделяемой памяти, который будет сохраняться до тех пор, пока не будет использован free.Вы можете присвоить значение tmp для *f и использовать его позже в main.Есть также использование для статических или глобальных переменных, которые сохраняются после возврата из функции.

0 голосов
/ 04 марта 2019

Это все виды разбитых.Ваша программа не указывает, что baz() работает так, как вы хотите.Это только кажется из-за другой поломки.

Переменная y в bar() является локальной для нее.Он и его хранилище действительны только при выполнении bar().После завершения bar(), y больше не действует и не является его хранилищем.И все же вы указали num на это хранилище.Это приведет к неопределенному поведению.Это просто случайность, когда хранилище не использовалось повторно или не перезаписывалось и продолжает удерживать значение 2 во время печати его значения.

baz() не влияет на то, на что указывает num.f в этой функции является параметром и, следовательно, локальным по отношению к baz().Присвоение f влияет только на этот локальный.

Итак, почему печать *num после вызова baz() дает "3"?Потому что num по-прежнему указывает на хранилище y от звонка на bar(), а звонок на baz() перезаписал это хранилище на 3. Опять же, это случайность и на него нельзя положиться.

Вам действительно нужно использовать указатель на указатель, чтобы повлиять на num, как вы это делали в bar().Но вы не должны использовать указатели на местных жителей после того, как вы вернулись из их области.

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