Для чего нужны указатели на указатели? - PullRequest
9 голосов
/ 07 января 2012

на языке программирования Go; как указатели на указатели могут стать полезными?

(Почему они не незаконны, если они не очень полезны?)

Ответы [ 5 ]

20 голосов
/ 07 января 2012

Полезность любого типа данных зависит от решаемой проблемы и метода, используемого для ее решения.Если тип данных не подходит к проблеме, он просто не подходит к проблеме - и в ней больше ничего нет.

Язык программирования Go (как и большинство других языков программирования) основан на простые правила, которые программист может использовать для создания новых типов данных.Вот некоторые из этих правил:

  • *T: создать новый тип данных, который является указателем на T
  • [10]T: массив Ts
  • struct { t T; u U ... }: структура, которая содержит T в качестве компонента
  • ...

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

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


И, наконец, ответ на ваш вопрос :

Тип **T обычно появляется в контекстах, где мы хотим перенаправить пользователей значения типа T на другое значение типа T, но по какой-то причине у нас нет доступа ко всем пользователямзначение или поиск пользователей будет стоить слишком много времени:

  1. Мы не хотим копировать значения типа T (по некоторым причинам)
  2. Мы хотим, чтобы все пользователизначение типа T для доступа к значению через указатель
  3. Мы хотим быстро перенаправить всех пользователей определенного значения типа T на другое значение

В такой ситуации использование **T является естественным, поскольку позволяет реализовать третий шаг в O (1):

type User_of_T struct {
  Value **T
}

// Redirect all users of a particular value of type T
// to another value of type T.
func (u *User_of_T) Redirect(t *T) {
  *(u.Value) = t
}
9 голосов
/ 07 января 2012

Когда вы передаете указатель на функцию, функция получает копию . Таким образом, присвоение нового значения данному указателю не приведет к присвоению его исходному:

type Smartphone struct {
    name string
}

type Geek struct {
    smartphone *Smartphone
}

func replaceByNG(s **Smartphone) {
    *s = &Smartphone{"Galaxy Nexus"}
}

func replaceByIPhone(s *Smartphone) {
    s = &Smartphone{"IPhone 4S"}
}

func main() {
    geek := Geek{&Smartphone{"Nexus S"}}
    println(geek.smartphone.name)

    replaceByIPhone(geek.smartphone)
    println(geek.smartphone.name)

    replaceByNG(&geek.smartphone)
    println(geek.smartphone.name)
}

Вывод:

Nexus S
Nexus S
Galaxy Nexus
4 голосов
/ 07 января 2012

В Си указатели на указатели довольно распространены. Например:

  • многомерных массивов (например, массив строк, char** argv может быть самым ярким примером здесь)
  • указатели в качестве выходных параметров

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

Второй пример, тем не менее, может применяться к программам Go. Допустим, у вас есть функция, которая должна иметь возможность изменять указатель, переданный в качестве параметра. В этом случае вам нужно будет передать указатель на этот указатель, чтобы вы могли изменить исходный указатель. Это очень часто встречается в C, потому что функции могут возвращать только одно значение (которое часто является своего рода кодом ошибки), и если вы хотите вернуть дополнительный указатель, вы должны будете использовать указатель на этот указатель в качестве выходного параметра. Однако функция в Go может возвращать несколько значений, поэтому появление указателей на указатели также встречается редко. Но они все еще могут быть полезными и в некоторых случаях могут привести к более приятному API.

Например, функция atomic.StorePointer может быть одним из тех редких, но хорошо спрятанных сценариев использования для указателей на указатели в стандартной библиотеке.

3 голосов
/ 03 февраля 2017

Линус Торвальдс недавно упомянул, как указатели на указатели приводят к коду с хорошим вкусом (на С). См. (Среди прочего) сообщение в блоге Брайана Барто .

1 голос
/ 07 января 2012

Вот пример, включающий ** прямо в стандартной библиотеке:

http://code.google.com/p/go/source/browse/src/pkg/encoding/gob/decoder.go#23

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