当前位置: 首页 > 面试题库 >

为什么Go中有数组?

谢烨烨
2023-03-14
问题内容

我了解Go中数组和切片之间的区别。但是我不明白的是为什么完全拥有数组会有所帮助。数组类型定义指定长度和元素类型为什么有帮助?为什么我们使用的每个“数组”都不能切片?


问题答案:

数组不仅仅是固定长度,还有更多:它们是
可比较的
,并且它们是
(不是引用或指针类型)。

在某些情况下,数组比切片有无数的优势,所有这些优点加在一起证明了数组的存在(以及切片)。让我们看看他们。(我什至不算数组是切片的基础。)

1. 具有可比性意味着 您可以将数组用作地图中的键 ,但不能 用作
切片。是的,您现在可以说为什么不让切片具有可比性,因此仅凭这一点并不能证明两者均存在。切片上没有很好地定义平等。常见问题解答:为什么地图不允许切片作为键?

他们没有实现平等,因为在此类类型上对平等的定义不明确。有多种考虑因素,包括浅层比较与深层比较,指针与值比较,如何处理递归类型等等。

2. 数组还可以为您提供更高的编译时安全性
,因为可以在编译时检查索引范围(数组长度必须计算为一个可以由type值表示的非负常量int):

s := make([]int, 3)
s[3] = 3 // "Only" a runtime panic: runtime error: index out of range

a := [3]int{}
a[3] = 3 // Compile-time error: invalid array index 3 (out of bounds for 3-element array)

3. 同样, 传递或分配数组值也将隐式创建 整个数组 的副本 ,因此它将与原始值“分离”。如果您传递一个切片,它将仍然仅复制切片
标头
,但切片值(标头)将指向相同的后备数组。这可能是您想要的,也可能不是。如果要从“原始”切片“分离”切片,则必须显式复制内容,例如使用内置copy()函数复制到新切片。

a := [2]int{1, 2}
b := a
b[0] = 10 // This only affects b, a will remain {1, 2}

sa := []int{1, 2}
sb := sa
sb[0] = 10 // Affects both sb and sa

4. 同样,由于数组长度是数组类型的一部分,所以 长度不同的数组是不同的类型
。一方面,这可能是“痛苦中的事情”(例如,编写一个带有类型参数的函数[4]int,您不能使用该函数来处理和处理类型数组[5]int),但这也可能是一个优势:可以用于
显式指定 所需数组 的长度
。例如,您想编写一个使用IPv4地址的函数,可以使用type对其进行建模[4]byte。现在,您有了编译时的保证,即传递给您的函数的值将恰好具有4个字节,不多也不少(无论如何这都是无效的IPv4地址)。

5. 与之前的内容有关, 数组长度也可以用于记录目的
。类型[4]byte正确记录了IPv4有4个字节。一个rgb类型的可变[3]byte告诉有对每个颜色成分1个字节。在某些情况下,甚至可以将其取出并单独记录;例如在crypto/md5包中:md5.Sum()返回类型为的值,[Size]byte其中md5.Size一个常数为16:MD5校验和的长度。

6.计划结构类型的内存布局
时,它们也非常有用,请参阅此处的JimB答案,以及更多详细信息和实际示例。

7. 同样,由于切片是标头,并且它们(几乎)总是按原样传递(没有指针), 因此语言规范对切片的指针比对数组的指针更具限制性
。例如,规范提供了多个用于操作数组指针的简写,而在切片时会给出编译时错误(因为很少使用指向切片的指针,如果仍然需要/必须这样做,则必须明确处理;)

这样的例子是:

  • 分割p指向array:的指针p[low:high]是的简写(*p)[low:high]。如果p是指向slice的指针,则为编译时错误(规范:slice表达式)。

  • 索引p数组指针:p[i]是的简写(*p)[i]。如果pis是指向切片的指针,则这是编译时错误(规范:Index expressions)。

例:

pa := &[2]int{1, 2}
fmt.Println(pa[1:1]) // OK
fmt.Println(pa[1])   // OK

ps := &[]int{3, 4}
println(ps[1:1]) // Error: cannot slice ps (type *[]int)
println(ps[1])   // Error: invalid operation: ps[1] (type *[]int does not support indexing)

8. 访问(单个)数组元素 比访问切片元素 更有效 ;像分片一样,运行时必须经过隐式指针取消引用。另外, “如果表达式
的类型是数组或指向数组的指针,则表达式len(s)cap(s)为常量s

可能令人惊讶,但您甚至可以写:

type IP [4]byte

const x = len(IP{}) // x will be 4

它是有效的,即使IP{}不是常量表达式,const i = IP{}也要进行评估和编译时,因此例如将是编译时错误!在此之后,以下操作也就不足为奇了:

const x2 = len((*IP)(nil)) // x2 will also be 4

注意:在一个完整的数组与一个完整的切片之间进行测量时,可能根本没有性能上的差异,因为显然可以对其进行优化,以使切片头中的指针仅被取消引用一次。有关详细信息/示例,请参阅数组与切片:访问速度。

必须阅读的博客:

切成薄片:用法和内部原理

数组,切片(和字符串):“追加”的机制



 类似资料:
  • 问题内容: 免责声明:我现在只和Go玩了一天,所以我很有可能错过了很多。 有谁知道为什么Go中没有对泛型/模板/ whatsInAName的真正支持?因此,有一个通用的,但这是由编译器提供的,而Go程序员无法编写自己的实现。关于如何使Go尽可能正交的所有讨论,为什么我可以使用通用类型而不创建新类型? 尤其是在函数式编程方面,有lambda甚至闭包,但是在缺少泛型的静态类型系统中,我该如何编写泛型高

  • 问题内容: 我惊讶地发现Go带有“ goto”语句。我一直被教导说,“ goto”语句已经成为过去,并且因为它阻塞了程序的实际流程,所以它变得邪恶,而函数或方法始终是控制流程的更好方法。 我肯定错过了什么。Google为什么要包含它? 问题答案: 当我们实际检查Go标准库的源代码时,我们可以看到s在哪些地方得到了很好的应用。 例如,在文件中,使用以下语句: 在从导入只是控制流使用的另一(布尔值)的

  • 问题内容: 在Go中,有可能等同于或取决于系统架构的类型。我可以声明一个整数变量而不必担心它的大小: 为什么没有type ,它等于或取决于我的系统的体系结构?我希望我也可以: 问题答案: float已在版本2011/01/20中删除。 您仍然可以使用简短的变量声明: 但正如GO常见问题解答所述: 出于可移植性的原因,我们决定以代码中的一些显式转换为代价,使事情变得清晰明了。 您可以在以下主题中查看

  • 问题内容: 我是Go编程语言的新手。 我注意到Go中有一些奇怪的地方:我认为它在Python中使用并替代了,但是当我在Go中使用时,它也是可行的。 和之间有什么区别? 问题答案: 是分配。有关Go中分配的更多信息:分配 和在变量声明中使用时的细微差别。 Go中变量声明的一般形式是: 上面的声明创建一个特定类型的变量,为其添加名称,并设置其初始值。 无论是或可以省略,但不能同时使用。 例如: 被称为

  • 问题内容: 使用该工具一段时间后,它看起来像: (可选)下载, 编译, 并安装 一个软件,而仅仅是 编译 并安装 它。在这种情况下,为什么该命令被取代而存在? 问题答案: 是本地工作时工作流程的一部分。假设您要使用库,但是由于某些原因需要进行更改。您可以这样做: ,仅下载该文件; 对下载的软件包进行更改; 安装本地版本。 据我知道有没有标志,以表明它应该 不 下载,所以它不能代替在这里。 从头开始