【问题标题】:In terms of design and when writing a library, when should I use a pointer as an argument, and when should I not?在设计和编写库时,什么时候应该使用指针作为参数,什么时候不应该?
【发布时间】:2016-01-25 20:00:01
【问题描述】:

对不起,如果我的问题看起来很愚蠢。我的背景是 PHP、Ruby、Python、Lua 和类似的语言,对现实生活中的指针一无所知。

根据我在互联网上阅读的内容以及对我提出的问题 (When is a pointer idiomatic?) 的回答,我了解到:

  • 复制大数据时应使用指针。无需获取整个对象层次结构,而是接收其地址并访问它。
  • 如果结构上有函数可以修改它,则必须使用指针。

因此,指针似乎是一件很棒的事情:我应该始终将它们作为函数参数获取,因为它们非常轻量级,而且如果我最终不需要修改结构上的任何内容也没关系。

但是,直观地看那句话,我觉得这听起来很令人毛骨悚然,但我不知道为什么。

那么,作为一个正在设计结构及其相关函数或只是函数的人,我应该什么时候收到指针?我应该什么时候收到一个值,为什么?

换句话说,我的NewAuthor 方法应该什么时候返回&Author{ ... },什么时候应该返回Author{ ... }?我的函数什么时候应该获得指向作者的指针作为参数,什么时候应该只获得Author 类型的值(副本)?

【问题讨论】:

  • 在 Go 中,如果我打算让我的类型的实例不可变,我只会选择指针上的值。在我看来,指针几乎总是更好,而不仅仅是在 Go 中。
  • 我来自类似的背景(Python,它没有明确的指针)并且已经接受了@evanmcdonnal 给出的相同建议。如果您没有 explicitly 不可变类,则应通过指针传递。
  • 另外,如果你的不可变值很大,仍然传递指针并且只在接口中提供getter。

标签: pointers go


【解决方案1】:

指针和值需要权衡取舍。

一般来说,指针将指向系统中的其他一些内存区域。无论是函数的堆栈想要将指针传递给局部变量还是堆上的某个位置。

func A() {
    i := 25
    B(&i) // A sets up stack frame to call B,
          // it copies the address of i so B can look it up later.
    // At this point, i is equal to 30
}
func B(i *int){
     // Here, i points to A's stack frame.
     // For this to execute, I look at my variable "i", 
     //   see the memory address it points to, then look at that to get the value of 25.
     // That address may be on another page of memory, 
     // causing me to have to look it up from main memory (which is slow).
     println(10 + (*i)) 

     // Since I have the address to A's local variable, I can modify it.
     *i = 30
}

每当我要查看指针指向的数据时,我都需要不断地取消引用它们。有时你不在乎。其他时候很重要。这真的取决于应用程序。

如果必须多次取消引用该指针(即:您传入一个数字以在一堆不同的计算中使用),那么您继续付出代价。

与使用值相比:

func A() {
    i := 25
    B(i) // A sets up the stack frame to call B, copying in the value 25
    // i is still 25, because A gave B a copy of the value, and not the address.
}
func B(i int){
     // Here, i is simply on the stack.  I don't have to do anything to use it.
     println(10 + i) 

     // Since i here is a value on B's stack, modifications are not visible outside B's scpe
     i = 30
}

由于没有什么可解引用的,所以使用局部变量基本上是免费的。

如果这些值很大,则会发生传递值的不利方面,因为将数据复制到堆栈不是免费的。

对于一个 int 来说,这是一个清洗,因为指针是“int”大小的。对于结构或数组,您正在复制所有数据。

此外,堆栈上的大对象会使堆栈变得特别大。 Go 通过堆栈重新分配很好地处理了这个问题,但在高性能场景中,它可能对性能影响太大。

还有一个数据安全方面(不能修改我通过值传递的东西),但我认为这在大多数代码库中通常不是问题。

基本上,如果您的问题已经可以通过 ruby​​、python 或其他没有值类型的语言解决,那么这些性能细微差别就无关紧要了。

一般来说,将结构体作为指针传递通常会在学习语言时做“正确的事情”。

对于所有其他类型,或您希望保留为只读的内容,请传递值。

这条规则也有例外,但最好是在需要时了解这些情况,而不是试图一次性重新定义你的世界。如果这有意义的话。

【讨论】:

    【解决方案2】:

    您可以在任何地方使用指针,有时您不想更改数据。它可能代表抽象数据,您不想显式复制数据。只需按值传递,让编译器完成它的工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-10
      • 2023-04-02
      • 2011-04-15
      • 2017-04-10
      • 2012-03-19
      • 2018-05-12
      相关资源
      最近更新 更多