何时使用指针是惯用的?

4

我来自一些没有显式指针的语言,所以我不太理解它们存在的意义(不是故意的双关语)。

问题在于,大多数时候我不知道为什么要将指针传递给函数。我确实明白当你传递一个指针时,对变量的修改会在任何地方都生效,但这有什么意义呢?为什么不直接修改值并返回结果呢?

例如,http.HandlerFunc 是一个接收 http.ResponseWriter*http.Request 作为参数的函数。我已经读到接口实际上是指针(这是正确的吗?),但我不明白的是,为什么?

我为什么要得到一个 writer 指针?我只是写入它,而不是修改它。还有,为什么我要得到一个 request 指针?我会使用像 request.FormValue() 这样的方法。

通过这些例子,我试图确定的问题是,“什么时候需要传递指针?”

我现在的做法是编写代码,尝试编译它,修复说必须传递指针的错误,通过添加一个&和星号,直到错误消失。然而,我感觉这个半理解的指针概念很快会让我吃亏。


接口实现使用指针,但接口本身不是指针。 - JimB
3个回答

2
你可以将指针视为指向对象的内存地址的值。与大多数数据结构相比,指针很小(例如8个字节)。
很多时候,你会得到一个指向对象的指针,因为传递这8个字节比创建要传递的整个对象的副本要快得多。
在请求对象的情况下,与仅传递具有对原始数据的访问权限的指针相比,创建请求下的所有内容(有效负载、标头等)的副本非常昂贵。

2
请注意,不要假定复制指针总是比复制结构体更快。由于CPU缓存等原因,在某些情况下,复制指针可能会更慢。 - Rick-777

1
一个指针,顾名思义,是指向内存中某个位置的变量。
在某些情况下,当复制整个对象的值不合理时,您可以使用它。这可能是因为对象太大,复制它会很慢,所以您只想传递一个指向对象的小指针并在内存中使用同一对象(可能是 http.Request 是指针的原因),或者因为您需要修改对其他代码中可能已经引用了对象的现有引用。您可以查看 net/http 的代码 here 以了解原因。 (实际上,对于任何 Go 标准库,如果您想查看为什么或如何以某种方式执行某些操作,则该代码是开放的。)
但这有点无关紧要,因为作为用户,您传递指针的原因是函数被定义为将指针作为参数。编写库的程序员决定使用指针的原因并不重要。您可以查找 go doc,或者像您正在做的那样,只需传递一个值,如果编译器抱怨,请修复它。

-1

当你理解指针的工作原理时,指针是一个非常简单的概念,但在理解之前,它看起来确实很难和烦人,特别是在C语言中。有指针算术、指向指针的指针等。

无论如何,如果你的库不强制使用指针,你就不必使用指针,但是在函数之间传递变量和数据结构可能会将所有东西复制到另一个内存区域,因为函数作用域的缘故。

在大多数语言中,值都是通过字面传递的。这意味着当你将一个变量传递给一个函数或类时,它可能会被复制。

更多的内存消耗,也需要CPU时间来复制项目。对于复杂性来说并不好。

另一方面,像PHP这样的一些语言具有一些自动化功能,例如写时复制算法,当你将参数传递给函数时,它通过引用传递,只传递内存地址到函数,直到函数尝试写入此变量时,它才会复制变量。

但自动化也带来了一些成本。真正的重点在于成本,Go试图尽可能快地运行,减少自动化,增加用户控制,但也试图保持安全和简单。不像C、C++那样让你感到羞耻。

在你的例子中,当你通过内存引用(作为指针)将http.ResponseWriter传递给任何函数时,在此函数内部写入内容,它直接写入其中,没有特殊的算法或检查是否存在类型不匹配等。可能会有一些像垃圾收集器之类的检查,但它确实带来了比成本更高的优势。
此外,在PHP中,Java对象是通过它们的引用传递的,但语言只隐藏了这种冗长性。

我读到接口实际上是指针

不完全正确,Interface{}是Go中的基础类型,因此每种类型都实现了接口类型,在这个观点中,它只是一种类型,所以称其为指针没有意义。 它为您提供了动态性,例如:如果您从未结构化且不一致的源中读取数据,结构可能随时更改,例如XML或JSON源。只需使用接口!您已经知道每种类型的祖先是接口,它将在运行时处理数据的底层结构/类型。
但是你也可以使用接口术语来定义一个接口(像Java、C#、PHP等),并提供实现方法。当任何满足这些方法的结构体时,它自然地实现了该接口。这种实现设计被称为鸭子类型,请阅读更多信息https://en.wikipedia.org/wiki/Duck_typing

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接