【问题标题】:Using a pointer to array使用指向数组的指针
【发布时间】:2011-01-27 05:20:18
【问题描述】:

我正在玩 google 的 Go 语言,我遇到了一些 C 语言中相当基本的东西,但我目前看到的文档中似乎没有涉及

当我将指向切片的指针传递给函数时,我认为我们有一些方法可以访问它,如下所示:

func conv(x []int, xlen int, h []int, hlen int, y *[]int)

    for i := 0; i<xlen; i++ {
        for j := 0; j<hlen; j++ {
            *y[i+j] += x[i]*h[j]
        }
    }
 }

但是 Go 编译器不喜欢这样:

sean@spray:~/dev$ 8g broke.go
broke.go:8: invalid operation: y[i + j] (index of type *[]int)

很公平 - 这只是一个猜测。我有一个相当简单的解决方法:

func conv(x []int, xlen int, h []int, hlen int, y_ *[]int) {
    y := *y_

    for i := 0; i<xlen; i++ {
        for j := 0; j<hlen; j++ {
            y[i+j] += x[i]*h[j]
        }
    }
}

但肯定有更好的方法。烦人的是,在 Go 上搜索信息并不是很有用,因为大多数搜索词都会出现各种 C/C++/不相关的结果。

【问题讨论】:

  • 问Go语言的问题,标记go,会有更多人回答; Vatine 为你做到了。 golang 是一个过时的标签;如您所见,有 117 个问题被标记为 go,只有 3 个问题被错误地标记为 golang
  • 请注意,您不需要 xlen 和 hlen 作为参数;您可以改用 len(x) 和 len(h)。
  • *y 放到() => (*y)[index] 玩得开心,见play ground

标签: pointers go


【解决方案1】:

Google Go 文档 state the following about passing arrays - 他们说您通常想要传递切片(而不是指针?):

更新:

正如@Chickencha 的评论所指出的,数组切片是引用,这就是它们有效传递的原因。因此,您可能希望使用切片机制而不是“原始”指针。

来自 Google Effective Go 文档 http://golang.org/doc/effective_go.html#slices

切片是引用类型,


原创

在标题下

关于类型的插曲

[...snip...] 传递数组时 对于一个函数,你几乎总是想要 将形参声明为 一片。当你调用函数时, 获取数组的地址并执行 将(有效地)创建一个切片 参考并通过。

编者注:不再是这种情况

使用切片可以编写这个函数(来自 sum.go):

09    func sum(a []int) int {   // returns an int
10        s := 0
11        for i := 0; i < len(a); i++ {
12            s += a[i]
13        }
14        return s
15    }

并像这样调用它:

19        s := sum(&[3]int{1,2,3})  // a slice of the array is passed to sum    

也许将整个数组作为切片传递。谷歌表示 Go 可以有效地处理切片。这是该问题的替代答案,但也许是更好的方法。

【讨论】:

  • 我同意这听起来像是要走的路。切片基本上已经是指向数组的指针,并且是通过引用传递数组的首选方式。
  • 附加在没有星号的情况下不起作用。见playground
  • @John K,即使是像指针这样的参考作品,你也不能混淆它们。您调用函数“sum”的示例代码将不会被编译,因为您不能将指针传递给需要切片(引用)的函数。调用函数“sum”s := sum(&amp;[3]int{1,2,3}) 的代码可以更改为 `arr :=[3]int{1,2,3} s := sum(arr[:]) ` 完整示例可以在 @987654324 上找到@
【解决方案2】:

[] 为空的类型,例如 []int,实际上是切片,而不是数组。在 Go 中,数组的大小是类型的一部分,所以要真正拥有一个数组,你需要有类似[16]int 的东西,指向它的指针是*[16]int。因此,您实际上已经在使用切片,并且指向切片的指针*[]int 是不必要的,因为切片已经通过引用传递。

还请记住,您可以使用 &amp;array 轻松传递一个引用整个数组的切片(只要切片的元素类型与数组的元素类型匹配)。(现在不行了。)

例子:

package main
import "fmt"

func sumPointerToArray(a *[8]int) (sum int) {
    for _, value := range *a { sum += value }
    return
}
func sumSlice (a []int) (sum int) {
    for _, value := range a { sum += value }
    return
}
func main() {
    array := [...]int{ 1, 2, 3, 4, 5, 6, 7, 8 }
    slice := []int{ 1, 2, 3, 4 }
    fmt.Printf("sum arrray via pointer: %d\n", sumPointerToArray(&array))
    fmt.Printf("sum slice: %d\n", sumSlice(slice))
    slice = array[0:]
    fmt.Printf("sum array as slice: %d\n", sumSlice(slice))
}

编辑:更新以反映自首次发布以来 Go 的变化。

【讨论】:

  • 哦,作为附加说明,数组长度的 [...] 符号只能应用于数组字面量,以使编译器为您计算元素。例如,不能将 *[...]int 作为函数的形式参数。
  • O_o 这不起作用:不能在函数参数中使用 &array (type *[8]int) 作为 type []int
  • @Doug 有趣的是,当我发布它时它确实编译了,旧文档也使用&amp;array 语法将数组作为切片传递。但是,它似乎不再起作用了。
  • @Doug 已更新为使用当前版本的 Go 进行编译。
  • 在搞砸之后,结果 []int 仍然有效,但是您必须在传递 args (sumPointerToArray(array[:]) 时手动将数组转换为切片,而不仅仅是传递 &array。
【解决方案3】:

添加和删除分号和星号。

*y[i+j] += x[i]*h[j]

被解释为

(*y)[i+j] += x[i] * h[j];

编辑:请阅读 cmets。答案可能不再有效。而且我已经有一段时间没有接触到 go 了,甚至无法再阅读此内容了。

【讨论】:

  • 在 Go 中你可以跳过分号,星号是我认为应该使用指向数组的指针。
  • 去掉分号。从 2009-12-22 版本开始:自上一个版本以来,该语言发生了一个很大的语法变化,已经在此列表中进行了广泛讨论:现在在语句结束标记和换行符之间隐含分号。详情请见groups.google.com/group/golang-nuts/t/5ee32b588d10f2e9
  • 请修复 MD :) - 现在看起来像一些外星字形哈哈(我自己会更改它,但由于某种原因编辑队列已满)
【解决方案4】:

长度是数组类型的一部分,可以通过内置函数len()获取数组的长度。所以你不需要传递 xlen、hlen 参数。

在 Go 中,您几乎总是可以在将数组传递给函数时使用 slice。在这种情况下,您不需要指针。 实际上,您不需要传递 y 参数。这是C的输出数组的方式。

Go 风格:

func conv(x, h []int) []int {
    y := make([]int, len(x)+len(h))
    for i, v := range x { 
        for j, u := range h { 
            y[i+j] = v * u 
        }   
    }   
    return y
}

调用函数:

conv(x[0:], h[0:])

【讨论】:

  • i 的值为 0 到 (len(x)-1),j 的值为 0 到 (len(h)-1)。因此,(i+j)的值是0到((len(x)-1)+(len(h)-1))。由于数组索引从零开始,我们不需要 ((len(x)-1)+(len(h)-1)+1) 数组元素来表示 y 吗?
  • 对于var s []int,引用ss[0:len(s)]s[0:]都是同一个东西,切片s。所以,更简洁,写conv(x, h),而不是conv(x[0:], h[0:])
【解决方案5】:

这是一个正在运行的 Go 程序。

package main

import "fmt"

func conv(x, h []int) []int {
    y := make([]int, len(x)+len(h)-1)
    for i := 0; i < len(x); i++ {
        for j := 0; j < len(h); j++ {
            y[i+j] += x[i] * h[j]
        }
    }
    return y
}

func main() {
    x := []int{1, 2}
    h := []int{7, 8, 9}
    y := conv(x, h)
    fmt.Println(len(y), y)
}

为避免错误猜测,请阅读 Go 文档:The Go Programming Language.

【讨论】:

    【解决方案6】:

    这个问题的几乎所有其他答案都在谈论使用切片而不是 数组指针,但没有人回答如何解决错误,所以我想我会 写下这个答案。该错误提示我们它无法访问 y 因为这是一个无效的操作。

    您的第一种方法是错误的,因为 Go 编译器对您大喊大叫。问题在 第一种方法是 *y[i+j] 是错误的语法。这是因为 从技术上讲,你正在这样做*(y[i+j]),你不能这样做,因为 y 在您的情况下是指向 int 数组的指针。如果您打印y,它将打印 数组的内存地址。

    您正在尝试获取不存在的yi+jth 索引 因为y 不是数组。您可以通过在 表示您正在尝试获取的 i+jth 索引的语句 y 指向的数组。使用(*y)[i+j] 而不是*y[i+j]。这 进行更改后,函数将如下所示:

    func conv(x []int, xlen int, h []int, hlen int, y *[]int) {
        for i := 0; i<xlen; i++ {
            for j := 0; j<hlen; j++ {
                (*y)[i+j] += x[i]*h[j]
            }
        }
    }
    

    【讨论】:

    • 您的代码使用指向切片的指针,而不是您的解释所暗示的数组
    • @Pizzalord 哦,是的,抱歉,我错误地使用了 array 而不是 slice,因为问题错误地使用了 array。我将在答案中将 array 替换为 slice,但该代码适用于切片和数组。
    猜你喜欢
    • 2015-06-17
    • 2021-02-07
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多