指针之所以有用有几个原因。指针允许控制内存布局(影响 CPU 缓存的效率)。在 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 等没有这个问题,因为它不允许你嵌入复合类型,所以不需要在语法上区分嵌入和指向。
使用 Go 指针解决的 Swift/C# 结构问题
实现相同目的的一种可能替代方法是像 C# 和 Swift 那样区分 struct 和 class。但这确实有局限性。虽然您通常可以指定函数将结构作为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 一直无法在 Google 等地方完全取代 C++ 进行系统编程,部分原因是由于缺乏控制内存布局和使用的能力(缓存未命中会影响性能),因此无法将性能调整到相同的范围显着地)。 Go 的目标是在许多领域取代 C++,因此需要支持指针。