Какой смысл в указателях в Go?

100

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

Я что-то упускаю или указатели в Go - ненужное усложнение?

анон
источник
1
Вот вопрос, который может помочь прояснить ситуацию : stackoverflow.com/questions/795160/… Существует разница между передачей ссылок по значению и передачей по ссылке.
Р. Мартиньо Фернандес,
1
Примечание: вопрос касается Java, но он применим и здесь.
Р. Мартиньо Фернандес,
1
«а для некоторых встроенных типов, таких как карты и каналы, неявная передача по ссылке». Нет, в Go все проходит по стоимости. Некоторые типы (неформально называемые) являются ссылочными типами, поскольку имеют внутреннее изменяемое состояние.
newacct
Проблема с этим вопросом в том, что «ссылки» не являются чем-то одним с четко определенными свойствами. Термин «ссылки» очень расплывчатый. Из ответов видно, сколько людей по-разному вкладывают в слово «ссылки». Таким образом, в этом вопросе следует уточнить, какие именно различия существуют между указателями Go и ссылками, которые имеет в виду вопрос.
mtraceur,

Ответы:

36

Мне очень нравится пример из http://www.golang-book.com/8

func zero(x int) {
    x = 0
}
func main() {
    x := 5
    zero(x)
    fmt.Println(x) // x is still 5
}

в отличие от

func zero(xPtr *int) {
    *xPtr = 0
}
func main() {
    x := 5
    zero(&x)
    fmt.Println(x) // x is 0
}
Петр Кочанский
источник
42
Вопрос был в том, «почему у нас есть указатели вместо ссылок », и я не понимаю, почему этот пример не работает со ссылками.
AndreKR 03
@AndreKR Потому что мы можем выбирать, передавать ли по ссылке или по значению. В некоторых случаях желательно и то, и другое.
JDSweetBeat
9
@DJMethaneMan Это «указатели против ссылок», а не «указатели против передачи по значению»!
AndreKR
В качестве дополнительного комментария в C # 2.0 была добавлена ​​передача по ссылке с помощью ключевого слова ref. Конечно, указатели все же более удобны в некоторых случаях, потому что мы можем иметь указатель на указатель на указатель ...
Робби Фэн
Я не понимаю, почему Go должен быть одним из самых простых популярных языков, и, тем не менее, они содержат такую ​​«особенность» ... Это сбивает с толку и кажется ненужным, по крайней мере, для людей, указывающих на это здесь.
Акито
33

Указатели полезны по нескольким причинам. Указатели позволяют контролировать структуру памяти (влияет на эффективность кеш-памяти ЦП). В Go мы можем определить структуру, в которой все члены находятся в непрерывной памяти:

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

В этом случае Pointструктуры встроены в LineSegmentструктуру. Но вы не всегда можете встроить данные напрямую. Если вы хотите поддерживать такие структуры, как двоичные деревья или связанный список, вам необходимо поддерживать какой-то указатель.

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode
}

Java, Python и т. Д. Не имеют этой проблемы, потому что они не позволяют вам встраивать составные типы, поэтому нет необходимости синтаксически различать встраивание и указание.

Проблемы со структурами Swift / C # решены с помощью указателей Go

Возможная альтернатива для достижения того же - различать structи classкак это делают C # и Swift. Но у этого есть ограничения. Хотя обычно вы можете указать, что функция принимает структуру в качестве inoutпараметра, чтобы избежать копирования структуры, это не позволяет вам хранить ссылки (указатели) на структуры. Это означает, что вы никогда не можете рассматривать структуру как ссылочный тип, если сочтете это полезным, например, для создания распределителя пула (см. Ниже).

Пользовательский распределитель памяти

Используя указатели, вы также можете создать свой собственный распределитель пула (это очень упрощено с удалением множества проверок, чтобы просто показать принцип):

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode

  nextFreeNode *TreeNode; // For memory allocation
}

var pool [1024]TreeNode
var firstFreeNode *TreeNode = &pool[0] 

func poolAlloc() *TreeNode {
    node := firstFreeNode
    firstFreeNode  = firstFreeNode.nextFreeNode
    return node
}

func freeNode(node *TreeNode) {
    node.nextFreeNode = firstFreeNode
    firstFreeNode = node
}

Поменять местами два значения

Указатели также позволяют реализовать swap. Это меняет местами значения двух переменных:

func swap(a *int, b *int) {
   temp := *a
   *a = *b
   *b = temp
}

Вывод

Java так и не смогла полностью заменить C ++ для системного программирования в таких местах, как Google, отчасти потому, что производительность не может быть настроена в той же степени из-за отсутствия возможности контролировать структуру и использование памяти (промахи в кэше значительно влияют на производительность). Go нацелен на замену C ++ во многих областях и, следовательно, должен поддерживать указатели.

Эрик Энгхейм
источник
7
C # позволяет передавать структуры по ссылке. См. Ключевые слова "ref" и "out".
olegz
1
Хорошо, это похоже на Свифт. Я подумаю, как обновить свой пример.
Эрик Энгхейм
29

Ссылки нельзя переназначить, в отличие от указателей. Уже одно это делает указатели полезными во многих ситуациях, когда ссылки использовать нельзя.

zildjohn01
источник
17
Возможность переназначения ссылок зависит от языка.
crantok 07
28

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


Язык программирования Go: вопросы и ответы по языковому дизайну: почему карты, фрагменты и каналы являются ссылками, а массивы - значениями?

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


Быстрая компиляция - основная цель разработки языка программирования Go; это имеет свои издержки. Одной из жертв, по-видимому, является возможность отмечать переменные (за исключением базовых постоянных времени компиляции) и параметры как неизменяемые. Был запрошен, но отклонен.


golang-nut: иди, язык. Некоторые отзывы и сомнения.

"Добавление const к системе типов заставляет его появляться повсюду и вынуждает удалять его везде, если что-то изменится. Хотя может быть некоторая выгода в том, чтобы отмечать объекты как неизменяемые каким-то образом, мы не думаем, что квалификатор типа const поможет идти."

Питер
источник
FWIW, «ссылочные типы» в Go также можно переназначить. Они больше похожи на неявные указатели?
Мэтт Джойнер
1
Это просто специальный синтаксис для структур, содержащих указатель (и длину, емкость и т. Д.).
mk12