为什么使用数组而不是切片?

lig*_*ght 17 arrays go slice

我一直在读Go,并且难以想到这个基本问题.

在Go中,很明显切片更灵活,当您需要一系列数据时,通常可以使用切片代替数组.

阅读大部分文档,他们似乎鼓励开发人员只使用切片而不是数组.我得到的印象就像创作者可以简单地设计数组来调整大小,并且没有整个切片部分.事实上,这样的设计会使语言更容易理解,甚至可能鼓励更多惯用的代码.

那么为什么创作者首先允许阵列呢?何时使用数组而不是切片?是否有过的情况下在片数组的使用将是引人注目的

当我查阅官方文档(http://golang.org/doc/effective_go.html#arrays)时,我发现的唯一有用的部分是:

在规划内存的详细布局时,数组很有用,有时可以帮助避免分配,但主要是它们是切片的构建块.

他们接着讨论了数组如何作为值的昂贵,以及如何用指针模拟C风格的行为.即便如此,他们还是明确地推荐了阵列部分:

但即便是这种风格也不是惯用的Go.改为使用切片.

那么,什么是"规划内存的详细布局"或"帮助避免分配"这些切片不适合的一些真实示例?

Ste*_*erg 17

正如Akavall所说,阵列是可以清洗的.这意味着它们可以用作地图的关键.

他们也是通过价值.每次将它传递给函数或函数或将其分配给另一个变量时,它都会生成一个完整的副本.

它们可以通过编码/二进制序列化.

它们还可用于控制内存布局.由于它不是引用,当它被放置在结构中时,它将分配那么多内存作为结构的一部分,而不是像切片那样将指针的等价物放在那里.

最重要的是,除非你知道自己在做什么,否则不要使用数组.


Hashable/serializable都很好用,但我不确定它们是否确实具有吸引力

如果你想要一张md5哈希的地图,你会怎么做?不能使用字节切片,所以你需要做这样的事情来绕过类型系统:

// 16 bytes
type hashableMd5 struct {a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p byte}
Run Code Online (Sandbox Code Playgroud)

然后为它创建一个序列化函数.Hashable数组意味着您可以将其称为[16]字节.

听起来像是接近C的malloc,sizeof

不,这与malloc或sizeof无关.那些是分配内存并获得变量的大小.

但是,CGo是另一个用例.cgo命令创建与其对应的C类型具有相同内存布局的类型.为此,有时需要插入未命名的数组以进行填充.

如果使用切片可以解决问题......使用切片进行无效/不显着的性能损失......

数组还可以防止间接更快地创建某些类型的代码.当然,这是一个很小的优化,几乎在所有情况下这都是微不足道的.


Aka*_*all 6

一个实际的区别是arrays可以清洗,而slices不是.

  • 如果结构包含不可散列的字段,则它们是不可散列的。 (2认同)

icz*_*cza 6

为了补充Stephen Weinberg的答案:

那么,什么是"规划内存的详细布局"或"帮助避免分配"这些切片不适合的一些真实示例?

这是"规划内存详细布局"的示例.有许多文件格式.通常文件格式是这样的:它以"幻数"开头,然后是信息标题,其结构通常是固定的.此标题包含有关内容的信息,例如在图像文件的情况下,它包含图像大小(宽度,高度),像素格式,使用的压缩,标题大小,图像数据偏移等相关信息(基本上描述了文件的其余部分)以及如何解释/处理它).

如果要在Go中实现文件格式,一种简单方便的方法是创建一个struct包含格式的头字段.当您想要读取这种格式的文件时,您可以使用该binary.Read()方法将整个标头读struct入变量,类似地,当您想要编写该格式的文件时,您可以使用binary.Write()一步将完整标头写入文件(或您发送数据的任何地方).

标题可能包含几十或一百个字段,您仍然可以使用一个方法调用来读/写它.

现在您可以感觉到,struct如果要一步完成所有操作,标题的"内存布局" 必须与文件格式完全匹配,因为它保存(或应保存)在文件中.

数组在哪里出现?

许多文件格式通常很复杂,因为它们希望是通用的,因此允许广泛的用途和功能.很多时候你不想实现/处理格式支持的所有东西,因为要么你不关心(因为你只想提取一些信息),要么你不必因为你有保证输入只会使用子集或固定格式(在文件格式完全支持的许多情况下).

那么,如果你有一个包含许多字段的标题规范但你只需要其中一些字段,你会怎么做?您可以定义一个包含所需字段的结构,在字段之间可以使用具有您不关心/不需要的字段大小的数组.这将确保您仍然可以通过一个函数调用读取整个标头,并且数组基本上将是文件中未使用数据的占位符.如果不使用数据,也可以在标题定义中使用空白标识符作为字段名称struct.

理论范例

举个简单的例子,让我们实现一个魔术是"TGI"(理论Go Image)的格式,标题包含这样的字段:2个保留字(每个16位),1个双字图像宽度,1个双字图像高度,现在来了15"不关心"dwords然后图像节省时间为自1970年1月1日UTC以来的8字节为纳秒.

这可以使用这样的结构建模(不包括幻数):

type TGIHeader struct {
    _        uint16 // Reserved
    _        uint16 // Reserved
    Width    uint32
    Height   uint32
    _        [15]uint32 // 15 "don't care" dwords
    SaveTime int64
}
Run Code Online (Sandbox Code Playgroud)

要读取TGI文件并打印有用信息:

func ShowInfo(name string) error {
    f, err := os.Open(name)
    if err != nil {
        return err
    }
    defer f.Close()

    magic := make([]byte, 3)
    if _, err = f.Read(magic); err != nil {
        return err
    }
    if !bytes.Equal(magic, []byte("TGI")) {
        return errors.New("Not a TGI file")
    }

    th := TGIHeader{}
    if err = binary.Read(f, binary.LittleEndian, &th); err != nil {
        return err
    }

    fmt.Printf("%s is a TGI file,\n\timage size: %dx%d\n\tsaved at: %v",
        name, th.Width, th.Height, time.Unix(0, th.SaveTime))

    return nil
}
Run Code Online (Sandbox Code Playgroud)

  • 这正是 cgo 命令在创建与其 C 对应物匹配的 Go 结构时所做的。赞成。 (2认同)