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