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

use*_*803 39 arrays arguments go

为什么这不起作用?

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

错误:

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

Jam*_*dge 45

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

  1. 调用函数时,从数组中创建一个切片.像这样改变呼叫就足够了:

    nameReader(a[:])
    
    Run Code Online (Sandbox Code Playgroud)
  2. 更改函数签名以获取数组而不是切片.例如:

    func nameReader(array [3]name) {
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)

    该解决方案的缺点是该函数现在只能接受长度为3的数组,并且在调用它时将生成该数组的副本.

您可以在此处找到有关阵列和切片的更多详细信息以及常见的陷阱:http://openmymind.net/The-Minimum-You-Need-To-Know-About-Arrays-And-Slices-In-Go /

  • Upvoted,但是`a:= [] name {"Abbed","Ahmad","Ghassan"}`是我首先将`a`定义为切片的方式. (2认同)
  • 我们应该在下一个版本的`func`签名中输入`[...]类型`:( (2认同)

jos*_*hlf 26

既然@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]"
Run Code Online (Sandbox Code Playgroud)

但是,正如您所注意到的,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]"
Run Code Online (Sandbox Code Playgroud)

履行

如果这个解释很容易理解,那么你可能也很想知道这是如何实现的(如果你无法理解这一点,我会在这里停止阅读,因为细节可能会让人感到困惑).

在引擎盖下,Go切片实际上是结构.他们有一个指向已分配内存的指针,就像我提到的那样,但它们还需要其他关键组件:长度和容量.如果在Go术语中描述它,它看起来像这样:

type int-slice struct {
    data *int
    len  int
    cap  int
}
Run Code Online (Sandbox Code Playgroud)

长度是切片的长度,它就在那里你可以要求len(mySlice),并且Go也可以检查以确保你没有访问实际上不在切片中的元素.然而,容量更令人困惑.让我们深入一点吧.

首次创建切片时,您需要提供切片所需的多个元素.例如,调用make([]int, 3)会给你一片3个整数.这样做是在内存中为3个int分配空间,然后返回一个带有指向数据的指针,长度为3,容量为3的结构.

但是,在Go中,您可以执行所谓的切片.这基本上是从旧切片创建新切片的位置,该切片仅表示旧切片的一部分.您可以使用slc[a:b]语法来引用slc从索引处开始并在索引a之前结束的子切片b.所以,例如:

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

这个切片操作在底层做的是制作对应的结构的副本a,并编辑指向内存中指向1整数的指针(因为新切片从索引1开始),并将长度编辑为2比之前更短(因为旧切片的长度为5,而新切片的长度为3).那么现在在内存中看起来是什么样的呢?好吧,如果我们可以想象出布局的整数,它看起来像这样:

  begin     end  // a
  v         v
[ 1 2 3 4 5 ]
    ^     ^
    begin end    // b
Run Code Online (Sandbox Code Playgroud)

请注意在结束之后还有一个int 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]"
Run Code Online (Sandbox Code Playgroud)

看看我们b[0:3]到底做了什么?此时的长度b实际上小于 3,所以我们能够做到这一点的唯一原因是Go已经跟踪了这样一个事实:在底层内存中,我们实际上已经保留了更多的容量.那样的话,当我们要求回来的时候,它可以很高兴.

  • 或者人们可以简单地阅读Rob的博客文章:[数组,切片(和字符串):'追加'的机制](http://blog.golang.org/slices). (4认同)

Xax*_*axD 7

另一种方法

可以在一个切片上调用一个可变参数函数来输入名称列表作为nameReader函数的单个参数,例如:

package main

import "fmt"

type name struct {
    X string
}

func main() {
    a := [3]name{{"Abbed"}, {"Ahmed"}, {"Ghassan"}}
    nameReader(a[:]...)
}

func nameReader(a ...name) {
    for _, n := range a {
        fmt.Println(n.X)
    }
}
Run Code Online (Sandbox Code Playgroud)