在 golang 中将数组作为参数传递

新手上路,请多包涵

为什么这不起作用?

 package main

import "fmt"

type name struct {
    X string
}

func main() {
    var a [3]name
    a[0] = name{"Abbed"}
    a[1] = name{"Ahmad"}
    a[2] = name{"Ghassan"}

    nameReader(a)
}

func nameReader(array []name) {
    for i := 0; i < len(array); i++ {
        fmt.Println(array[i].X)
    }
}

错误:

 .\structtest.go:15: cannot use a (type [3]name) as type []name in function argument

原文由 user1721803 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 800
2 个回答

您已将函数定义为接受一个切片作为参数,而您正试图在调用该函数时传递一个数组。有两种方法可以解决这个问题:

  1. 调用函数时从数组中创建一个切片。像这样更改调用就足够了:
    nameReader(a[:])

  1. 更改函数签名以采用数组而不是切片。例如:
    func nameReader(array [3]name) {
       ...
   }

这种解决方案的缺点是该函数现在只能接受长度为 3 的数组,并且在调用它时会制作该数组的副本。

您可以 在此处 找到有关数组和切片的更多详细信息,以及使用它们时的常见陷阱

原文由 James Henstridge 发布,翻译遵循 CC BY-SA 4.0 许可协议

由于@james-henstridge 的回答已经涵盖了如何让它发挥作用,我不会重复他所说的内容,但我会解释 为什么 他的回答有效。

在 Go 中,数组的工作方式与大多数其他语言略有不同(是的,有数组和切片。稍后我将讨论切片)。在 Go 中,数组是固定大小的,正如您在代码中使用的那样(因此, [3]int [4]int 不同的类型)。此外,数组是 _值_。这意味着如果我将一个数组从一个地方复制到另一个地方,我实际上是在复制该数组的所有元素(而不是像在大多数其他语言中那样,只是对同一个数组进行另一个引用)。例如:

 a := [3]int{1, 2, 3} // Array literal
b := a               // Copy the contents of a into b
a[0] = 0
fmt.Println(a)       // Prints "[0 2 3]"
fmt.Println(b)       // Prints "[1 2 3]"

然而,正如您所注意到的,Go 也有切片。切片类似于数组,除了两个关键方面。首先,它们是可变长度的(所以 []int 是任意数量整数的切片类型)。其次,切片是 _引用_。这意味着当我创建一个切片时,会分配一块内存来表示切片的内容,而切片变量本身实际上只是指向该内存的指针。然后,当我复制那个切片时,我实际上只是在复制指针。这意味着如果我复制切片然后更改其中一个值,我会为每个人更改该值。例如:

 a := []int{1, 2, 3} // Slice literal
b := a              // a and b now point to the same memory
a[0] = 0
fmt.Println(a)      // Prints "[0 2 3]"
fmt.Println(b)      // Prints "[0 2 3]"

执行

如果这个解释很容易理解,那么您可能也想知道它是如何实现的(如果您难以理解,我会停止阅读这里,因为细节可能会令人困惑)。

在幕后,Go 切片实际上是结构。它们有一个指向已分配内存的指针,就像我提到的,但它们还有另外两个关键组成部分:长度和容量。如果用 Go 术语来描述,它看起来像这样:

 type int-slice struct {
    data *int
    len  int
    cap  int
}

长度是切片的长度,它就在那里,这样你就可以请求 len(mySlice) ,而且 Go 可以检查以确保你没有访问实际上不在切片中的元素。然而,容量有点令人困惑。因此,让我们深入探讨一下。

当你第一次创建一个切片时,你会给出一些你想要切片的元素。例如,调用 make([]int, 3) 将为您提供 3 个整数的切片。它所做的是在内存中为 3 个整数分配空间,然后返回一个指向数据的指针结构,长度为 3,容量为 3。

然而,在 Go 中,你可以做所谓的 _切片_。这基本上是您从仅代表旧切片的一部分的旧切片创建新切片的地方。 You use the slc[a:b] syntax to refer to the sub-slice of slc starting at index a and ending just before index b .所以,例如:

 a := [5]int{1, 2, 3, 4, 5}
b := a[1:4]
fmt.Println(b) // Prints "[2 3 4]"

这个切片操作在幕后所做的是复制对应于 a 的结构,并编辑指向内存中指向 1 整数的指针(因为新切片从索引 1 开始),并将长度编辑为比以前短 2(因为旧切片的长度为 5,而新切片的长度为 3)。那么现在这在内存中是什么样子的呢?好吧,如果我们可以将整数布局可视化,它看起来像这样:

   begin     end  // a
  v         v
[ 1 2 3 4 5 ]
    ^     ^
    begin end    // b

请注意在 b 结束后还有一个整数?嗯,就是这个能力。看,只要记忆还在身边供我们使用,我们也可以使用所有的记忆。因此,即使您只有一个长度很小的切片,它也会记住还有更多容量,以防您需要它。所以,例如:

 a := []int{1, 2, 3}
b := a[0:1]
fmt.Println(b) // Prints "[1]"
b = b[0:3]
fmt.Println(b) // Prints "[1 2 3]"

看看我们怎么做 b[0:3] 到底有没有? b 的长度在这一点上实际上 小于 3,所以我们能够做到这一点的唯一原因是 Go 跟踪了一个事实,即在底层内存中,我们实际上已经保留了更多容量。那样的话,当我们要回其中的一部分时,它会很乐意帮忙。

原文由 joshlf 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题