在 Go 中使用指针有啥意义?
Posted
技术标签:
【中文标题】在 Go 中使用指针有啥意义?【英文标题】:What's the point of having pointers in Go?在 Go 中使用指针有什么意义? 【发布时间】:2010-12-24 05:20:54 【问题描述】:我知道 Go 中的指针允许改变函数的参数,但如果它们只采用引用(使用适当的 const 或 mutable 限定符),那岂不是更简单。现在我们有了指针和一些内置类型,如地图和通道,隐式通过引用传递。
我是否遗漏了什么,或者 Go 中的指针只是一个不必要的复杂问题?
【问题讨论】:
这里有一个问题可能有助于澄清:***.com/questions/795160/… 按值传递引用和真正按引用传递是有区别的。 注意:这个问题是关于 Java 的,但它也适用于这里。 "对于某些内置类型,如地图和通道,隐式通过引用传递。"不,在 Go 中一切都是按值传递的。有些类型(非正式地称为)引用类型,因为它们具有内部可变状态。 这个问题的问题在于,“引用”不是具有明确属性的单一事物。 “参考”一词非常模糊。我们可以在答案中看到有多少人将不同的东西读入“参考”这个词。所以这个问题应该详细说明 Go 指针和问题所考虑的引用之间到底有什么区别。 【参考方案1】:我不会在“Go”的上下文中回答这个问题,而是在任何实现“指针”概念的语言(例如 C、C++、Go)的上下文中回答这个问题;同样的推理也可以应用于“Go”。
内存分配通常有两个内存部分:堆内存和堆栈内存(我们不要包括“全局部分/内存”,因为它会脱离上下文)。
堆内存:这是大多数语言所使用的:Java、C#、Python……但它带有一个称为“垃圾收集”的惩罚,这会直接影响性能.
堆栈内存:变量可以在 C、C++、Go、Java 等语言的堆栈内存中分配。栈内存不需要垃圾回收;因此它是堆内存的一种高性能替代方案。
但是有一个问题:当我们在堆内存中分配一个对象时,我们会得到一个“引用”,它可以传递给“多个方法/函数”,它是通过引用, “多个方法/函数”可以直接读取/更新同一个对象(分配在堆内存中)。可悲的是,堆栈内存并非如此。正如我们所知,每当将堆栈变量传递给方法/函数时,只要您有“指针的概念”(如 C、C++、Go 的情况),它就会“按值传递”(例如 Java)。
这里是指针出现的地方。指针让“多个方法/函数”读取/更新放置在堆栈内存中的数据。
简而言之,“指针”允许使用“堆栈内存”而不是堆内存,以便通过“处理变量/结构/对象>多种方法/功能”;因此,避免了垃圾回收机制造成的性能损失。
【讨论】:
【参考方案2】:指针之所以有用有几个原因。指针允许控制内存布局(影响 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++,因此需要支持指针。
【讨论】:
C# 允许通过引用传递结构。请参阅“ref”和“out”关键字。 好吧,它就像 Swift。我会想办法更新我的例子。 第一点没看懂,左右不就可以是TreeNode吗?并且大多数类型化语言是否允许在不使用指针的情况下使用递归类型/接口? 不,它不能是 TreeNode,因为在结构内部为每个成员留出了空间。如果您在 TreeNode 中为整个 TreeNode 留出空间,您最终会得到一种占用无限空间的无限递归定义。您必须能够通过将左和/或右设置为空来终止树。 TreeNode 不能为空。只有 *TreeNode 这样的指针才能为空。 大多数语言允许递归结构,因为它们实际上使用指针。 Java 和许多其他人只是愚蠢地不调用它们的引用指针。它们是指针。大多数其他语言的问题是它们几乎只支持指针而不支持值类型。这意味着它们没有典型的指针操作,例如获取地址或取消引用。他们不能这样做,因为除了像 float 和 int 这样的原始值之外,他们没有其他值类型可以这样做。【参考方案3】:我真的很喜欢来自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
【讨论】:
问题是“为什么我们有指针而不是引用”,我不明白为什么这个例子不适用于引用。 @AndreKR 因为我们可以选择是通过引用传递还是通过值传递。在某些情况下,两者都可取。 @DJMethaneMan 这是“指针与引用”,而不是“指针与传递值”! 作为旁注,通过引用在 C# 2.0 中通过“ref”关键字添加。当然指针在某些情况下还是更方便的,因为我们可以将指针指向指针... 我不明白 Go 应该是如何成为最简单的流行语言之一,但它们却包含这样一个“功能”......这令人困惑并且似乎没有必要,至少对于指出这个的人来说在这里。【参考方案4】:Go 被设计成一种简洁、极简的语言。因此,它仅从值和指针开始。后来,根据需要,添加了一些引用类型(切片、地图和通道)。
The Go Programming Language : Language Design FAQ : Why are maps, slices, and channels references while arrays are values?
“关于这个话题有很多历史。早期,地图和通道在语法上是指针,不可能声明或使用非指针实例。此外,我们在数组应该如何工作方面苦苦挣扎。最终我们决定指针和值的严格分离使得语言更难使用。引入引用类型,包括切片来处理数组的引用形式,解决了这些问题。引用类型给语言增加了一些令人遗憾的复杂性,但它们对可用性有很大影响:引入 Go 后,Go 成为一种更高效、更舒适的语言。”
快速编译是 Go 编程语言的主要设计目标;这是有代价的。损失之一似乎是将变量(基本编译时常量除外)和参数标记为不可变的能力。有人要求,但被拒绝了。
golang-nuts : go language. Some feedback and doubts.
“将 const 添加到类型系统会强制它出现在任何地方,并且 如果有什么变化,它会强迫人们在任何地方移除它。在那里时 以某种方式标记对象不可变可能有一些好处,我们不 认为 const 类型限定符是可行的。”
【讨论】:
FWIW,Go 中的“引用类型”也可以重新分配。它们更像是隐式指针? 它们只是包含指针(以及长度、容量等)的结构的特殊语法。 您能否添加关于“什么是参考”的定义。我不知道该怎么想Later, by necessity, some reference types (slices, maps, and channels) were added
。他们不只是用附加的指针来重视吗?【参考方案5】:
引用不能被重新分配,而指针可以。仅这一点就使得指针在许多无法使用引用的情况下很有用。
【讨论】:
引用是否可重新分配是特定于语言的实现问题。以上是关于在 Go 中使用指针有啥意义?的主要内容,如果未能解决你的问题,请参考以下文章