Указатели полезны по нескольким причинам. Указатели позволяют контролировать структуру памяти (влияет на эффективность кеш-памяти ЦП). В 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 ++ во многих областях и, следовательно, должен поддерживать указатели.