Який сенс мати вказівники в Go?


100

Я знаю, що покажчики в Go дозволяють мутувати аргументи функції, але чи не було б простіше, якби вони прийняли лише посилання (з відповідними const або змінними кваліфікаторами). Зараз у нас є покажчики, а для деяких вбудованих типів, таких як карти та канали, неявний перехід за посиланням.

Мені чогось не вистачає чи покажчики в Go - це лише зайве ускладнення?


1
Ось питання, яке може допомогти прояснити: stackoverflow.com/questions/795160/… Існує різниця між передачею посилань за значенням та передачею справді за посиланням.
R. Martinho Fernandes

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

@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

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


17
Чи можна переназначати посилання - це проблема реалізації, яка залежить від мови.
crantok

28

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


Мова програмування Go: Поширені запитання щодо мовного дизайну: Чому карти, фрагменти та канали посилаються, а масиви є значеннями?

"Існує багато історії на цю тему. На початку карти та канали були синтаксичними вказівниками, і неможливо було оголосити або використовувати екземпляр, що не вказує. Також ми боролися з тим, як повинні працювати масиви. Зрештою ми вирішили, що суворе розділення покажчиків та значень ускладнив використання мови. Представлення посилальних типів, включаючи зрізи для обробки посилальної форми масивів, вирішило ці проблеми. Типи посилань додають мові певну складність, але вони мають великий вплив на зручність використання: Go став більш продуктивну, зручну мову, коли вони були введені ".


Швидка компіляція є основною метою проектування мови програмування Go; що має свої витрати. Однією з жертв, здається, є можливість позначення змінних (крім основних констант часу компіляції) та параметрів як незмінні. Це було прохано, але відхилено.


голанг-горіхи: go мова. Деякі відгуки та сумніви.

"Додавання const до системи типу змушує її з'являтися скрізь і змушує її видаляти її скрізь, якщо щось змінюється. Хоча може бути певна вигода від позначення об'єктів незмінними якимось чином, ми не вважаємо, що класифікатор типу const має шлях йти."


FWIW, "посилальні типи" в Go також можна перепризначити. Вони більше схожі на неявні вказівники?
Matt Joiner

1
Вони є лише спеціальним синтаксисом для структур, які містять покажчик (і довжину, місткість, ...).
mk12
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.