使用指针索引-指针如何与切片一起使用?

问题描述 投票:1回答:3

在下面的main()代码中:

package main

import (
    "fmt"

    "github.com/myhub/cs61a/poetry"
)

func main() {

    p := poetry.NewPoem([][]string{
        {
            "And from my pillow, looking forth by light",
            "Of moon or favoring stars, I could behold",
            "The antechapel where the statue stood",
            "Of Newton, with his prism and silent face,",
            "The marble index of a mind forever",
            "Voyaging through strange seas of thought, alone.",
        },
        {
            "inducted into Greek and Christian",
            "modes of thinking, must take a longer way around.",
            "Like children born into love, they exult in the here",
            "who have felt separation and grave misgivings, they",
            "and of humanity, and of God. Great literature, like",
            "struggles to reconcile suffering with faith in the ",
        },
    })

    fmt.Printf("%T\n", p[0])

}

[p[0]通过使用下面的函数构造函数指向第一个节可以正常工作:

package poetry

type Line string
type Stanza []Line
type Poem []Stanza

func NewPoem(s [][]string) Poem {
    var poem Poem
    for _, stanza := range s {
        var newStanza Stanza
        for _, line := range stanza {
            newStanza = append(newStanza, Line(line))
        }
        poem = append(poem, newStanza)
    }

    return poem
}

如果,NewPoem()返回类型为*Poem的值,如下所示:

package poetry

type Line string
type Stanza []Line
type Poem []Stanza

func NewPoem(s [][]string) *Poem {
    var poem Poem
    for _, stanza := range s {
        var newStanza Stanza
        for _, line := range stanza {
            newStanza = append(newStanza, Line(line))
        }
        poem = append(poem, newStanza)
    }

    return &poem
}

然后,p[0]中的main()给出以下错误:

     Invalid operation: p[0] (type *poetry.Poem does not support indexing)

为什么指向字符串切片的指针不支持p[0]语法?

pointers go slice
3个回答
6
投票

为什么指向字符串切片的指针不支持p[0]语法?

简短回答:因为语言规范不允许这样做。

更长的答案:因为很少使用指向切片的指针,为什么使该语言复杂而又没有什么好处呢?如果在极少数情况下确实需要它,则只需取消引用指针并为切片编制索引。

请注意,规范允许对数组指针进行索引或切片,因为使用数组时,指向数组的指针比指向切片的指针更频繁,更有用。在这里阅读更多有关它的信息:Slicing a slice pointer passed as argument


1
投票

为什么指向字符串切片的指针不支持p [0]语法?

因为指向(任何内容)切片的指针不是该语言定义为支持索引的类型。这是规格:

a[x]形式的主表达式表示数组的元素,指向数组,切片,字符串或映射x的索引的指针。值x分别称为索引或映射键。适用以下规则:

来源:https://golang.org/ref/spec#Index_expressions

继续,并明确定义了每种受支持类型的索引工作方式,最后说否则a [x]是非法的。

因此基本上您正在尝试执行该语言不允许的操作


关于原因,并谈到您的设计,切片的指针很少说得通

首先,当您传递一个切片时,移动的数据量绝对是最小的(它只是一个切片标头,通常是十几个字节,因此与传递指针相比,没有可见的增益。

然后,在您对问题的评论中说,“它似乎是提供指向Poem类型的指针的更好的抽象。”实际上,这取决于您要对类型Poem执行的操作。鉴于您提供的一些细节,我认为传递*Poem而不是仅传递Poem实际上是一个更糟糕的设计选择,因为您对指针一无所获,并且实际上使事情变得复杂。例如,您不能使用索引(p[0])...


在对此答案的评论中,您问:您看到[][]string{...}传递给NewPoem()的一种好方法吗?

就像我在评论中提到的那样,我们没有很多细节,因此很难说。但目前,您可以执行此操作以创建Poem

p := poetry.Poem{
    {
        "And from my pillow, looking forth by light",
        "Of moon or favoring stars, I could behold",
        "The antechapel where the statue stood",
        "Of Newton, with his prism and silent face,",
        "The marble index of a mind forever",
        "Voyaging through strange seas of thought, alone.",
    },
    {
        "inducted into Greek and Christian",
        "modes of thinking, must take a longer way around.",
        "Like children born into love, they exult in the here",
        "who have felt separation and grave misgivings, they",
        "and of humanity, and of God. Great literature, like",
        "struggles to reconcile suffering with faith in the ",
    },
}

fmt.Printf("%T\n", p[0])

不需要循环或附加或NewPoem函数或任何直接从字符串创建诗歌的东西。只需直接使用类型Poem{...}

[如果您需要做一些不同的事情,例如通过从文件中读取数据来创建一首诗,则可以创建类似这样的函数:

package poetry

func ReadFrom(rd io.Reader) Poem { ... }

但是只要直接在代码中直接创建一首诗,就不需要使事情复杂化。


0
投票

为什么指向字符串切片的指针不支持p [0]语法?

因为它是一个指针,所以它内部没有数组或切片之类的可索引元素。如果希望索引访问Stanza,可以取消引用指针(从而获得一个切片),然后访问切片中第0个元素:

fmt.Printf("%T\n", (*p)[0])

输出:

main.Stanza

或示例的完整代码:https://play.golang.org/p/7abmjp6AxA4

但是。您是否真的需要指向切片的指针? Golang中的切片是非常轻量级的结构-由3个64位值组成:指向具有数据的数组的指针,该数组的当前长度和该数组的最大长度-所谓的切片容量。

我们可以比较传递一个切片和传递一个指向它的指针:

  1. 24字节,您将获得一个数据驻留的地址

  2. 8个字节,但是您必须取消引用它-转到RAM中的另一个位置以获取切片(24个字节),然后获取数据的地址。这种间接操作可能会更昂贵。

通过切片足以满足99%的情况。即使使用OS方法从文件中读取数据,也将切片作为缓冲区传递。

f, _ := os.Open("/tmp/dat")

// Create a buffer of size 5
b1 := make([]byte, 5)

// Read some bytes from the beginning of the file. 
// Allow up to 5 to be read - this value is taken from slice length 
// Read returns how many bytes actually were read.
n, _ := f.Read(b1)

更多信息:https://gobyexample.com/reading-files

© www.soinside.com 2019 - 2024. All rights reserved.