Pointers 使用指针索引-指针如何使用切片?

Pointers 使用指针索引-指针如何使用切片?,pointers,go,slice,Pointers,Go,Slice,在下面的main()code中: 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

在下面的
main()
code中:

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
}
然后,
main()
中的
p[0]
给出以下错误:

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

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

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

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

较长的回答:因为很少使用指向切片的指针,为什么要用一些对许多人没有好处的东西使语言复杂化?如果在极少数情况下确实需要它,只需取消对指针的引用并为切片编制索引

请注意,规范允许对数组指针进行索引或切片,因为在使用数组时,指向数组的指针比指向切片的指针更经常、更有用。请在此处阅读更多信息:

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

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

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

资料来源:

它接着明确定义了索引如何为每个受支持的类型工作,最后指出,否则a[x]是非法的

因此,基本上您试图做一些语言不允许的事情


至于原因,再谈谈您的设计,指向切片的指针很少有意义

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

然后,在你对问题的评论中,你说,“提供一个指向诗歌类型的指针看起来是一个更好的抽象。”这实际上取决于你想对诗歌类型做什么。考虑到您提供的一些细节,我认为传递
*Poem
实际上是一个更糟糕的设计选择,而不仅仅是
Poem
,因为指针没有任何好处,而且实际上会使事情复杂化。例如,您不能使用索引(
p[0]


在对这个回答的评论中,您问:您是否看到
[]字符串{…}
传递给
NewPoem()
是一个好方法

就像我在评论中提到的,我们没有很多细节,所以很难说。但目前,您可以这样做来创建一首

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 { ... }
但是对于直接在代码中创建一首诗来说,没有必要把事情复杂化

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

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

fmt.Printf(“%T\n”,(*p)[0])
输出:

main.Stanza
或示例的完整代码:

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

我们可以将传递切片与向切片传递指针进行比较:

  • 24字节,您将获得数据所在的地址

  • 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)
    

    更多信息:

    @CeriseLimón很抱歉。。。。queryedited对于它的价值,当您传递切片时复制的内容是一个非常小的结构,引用内容(指针、长度和容量)。内容没有被复制。如果你是为了提高效率而传递一个指针,你可能不需要这样做。关于指针什么时候有帮助的整个问题会出现很多,答案会更长。直接的答案是,在索引到指针(*p)之前,需要取消对它的引用;看,我想找到节数、行数、元音数、辅音数、标点符号等…在上面的代码中,你是否看到
    [[]字符串{…}
    传递到
    NewPoem()
    是一个好方法?@overexchange-就设计而言,这些都是“只读”操作,因此在创建后你不需要修改诗歌。因此,您可以使
    Poem
    成为不可变的类型(这意味着您可以“免费”获得大量额外的好处)。这是您不使用指针的另一个原因。是的,指针类型被传递或返回以改变数据。我会附加一节later@overexchange-我将编辑我的答案来讨论
    []]字符串{…}
    这件事,因为注释可能不是最好的地方。@overexchange-即使您想在以后附加一节,也可能不需要指针。您可以改为使用如下内容:
    func(p-Poem)AddStanza(s-Stanza)Poem{…}
    ,它将返回一个新的、不可变的Poem,并带有额外的节。然后像这样使用它:
    var p Poem。。。;P