问题描述
我明白Go中数组和片的区别。但是我不明白的是为什么数组完全有帮助。为什么数组类型定义指定长度和元素类型会有帮助?为什么我们使用的每个数组都不是一个分片? ://golang.org/ref/spec#Array_typesrel =nofollow noreferrer>数组,而不仅仅是固定长度:,它们是值(不是引用或指针类型)。
在某些情况下,数组相对于数组具有无数的优点,所有这些优点都超过了证明数组存在(与切片一起)的理由。我们来看看他们。 (我甚至没有把数组当作片的构建块)。
1。可比较意味着你可以在地图中使用数组作为键,但不能使用切片。是的,你现在可以说,为什么不让切片与之相当,所以单凭这一点就不能证明两者的存在。平等在切片上没有很好的定义。
2。 数组也可以为您提供更高的编译时安全性,因为可以在编译时检查索引边界(数组长度必须评估为非负数可由类型 int 的值表示) :
s:= make([] int,3)
s [3] = 3 //Only运行时错误:运行时错误:索引超出范围
a:= [3] int {}
a [3] = 3 //编译时错误:数组索引3无效3元素数组的限制)
3。 传递围绕或分配数组值将隐式地创建整个数组的一个副本,所以它将与原始值分离。如果传递一个片段,它仍然会创建一个副本,但只是片段头部,但片段值(头部)将指向相同的支持数组。这可能是也可能不是你想要的。如果你想从原始中分离切片,你必须明确地复制内容,例如内置函数到一个新的切片。
a:= [2] int {1,2}
b:= a
b [0] = 10 //这只影响b,a将保持{1,2}
sa:= [] int {1,2,$}
sb:= sa
sb [0] = 10 //影响sb和sa
4 。另外,由于数组长度是数组类型的一部分,因此不同长度的数组是不同类型的。一方面,这可能是一个痛苦的屁股(例如,你写一个函数,它的参数类型为 [4] int ),你不能使用该函数以获取和处理 [5] int 类型的数组),但这也可能是一个优点:这可能用于显式指定长度预期的阵列。例如。你想写一个需要IPv4地址的函数,它可以用 [4] byte 类型建模。现在你有一个编译时间的保证,传递给你的函数的值将有正好4个字节,不多也不少(这将是一个无效的IPv4地址)。
5。与前面的内容相关,数组长度也可用于文档目的。类型 [4]字节恰当地表明IPv4有4个字节。 [3] byte 类型的 rgb 变量告诉每个颜色分量有1个字节。在某些情况下,它甚至被取出并可用,分开记录;例如在返回一个类型为 [Size] byte 的值,其中 md5.Size 是一个常量< 16 :MD5校验和的长度。
6。 strong>规划内存布局的结构类型,请参阅JimB的答案,并。
7。另外,由于切片是标题,它们(几乎)总是按原样传递(无指针) 语言规范对指向切片的指针比指向数组的指针更严格。例如,该规范提供了用于指向数组的操作的多个shorthands,而在切片的情况下,这给出了编译时错误(因为很少使用指向slice的指针,如果您仍然需要/必须这样做,您必须明确的处理它;阅读更多在)。
这样的例子是: 将指针指向数组: p p [低:高] 是(* p)[低:高] 的简写。如果 p 是一个指向slice的指针,这是编译时错误()。 索引 p 数组: p [i] 是(* p)[i] 的简写。如果 p 是指向切片的指针,这是编译时错误(,例如 const i = IP {} 会是编译时错误!在这之后,下面的内容也不足为奇: 请参阅相关问题,其中数组可以使用/更有意义:
p>
const x2 = len((* IP)(nil)) // x2也将是4
这只是为了好奇: 。 (实际上这个属性使得比较更容易,因为你不必处理递归数据结构)。
必读博客:
p>
I understand the difference between arrays and slices in Go. But what I don't understand is why it is helpful to have arrays at all. Why is it helpful that an array type definition specifies a length and an element type? Why can't every "array" that we use be a slice?
There is more to arrays than just the fixed length: they are comparable, and they are values (not reference or pointer types).
There are countless advantages of arrays over slices in certain situations, all which together more than justify the existence of arrays (along with slices). Let's see them. (I'm not even counting arrays being the building blocks of slices.)
1. Being comparable means you can use arrays as keys in maps, but not slices. Yes, you could say now that why not make slices comparable then, so that this alone wouldn't justify the existence of both. Equality is not well defined on slices. FAQ: Why don't maps allow slices as keys?
2. Arrays can also give you higher compile-time safety, as the index bounds can be checked at compile time (array length must evaluate to a non-negative constant representable by a value of 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. Also passing around or assigning array values will implicitly make a copy of the entire array, so it will be "detached" from the original value. If you pass a slice, it will still make a copy but just of the slice header, but the slice value (the header) will point to the same backing array. This may or may not be what you want. If you want to "detach" a slice from the "original" one, you have to explicitly copy the content e.g. with the builtin copy() function to a new slice.
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. Also since the array length is part of the array type, arrays with different length are distinct types. On one hand this may be a "pain in the ass" (e.g. you write a function which takes a parameter of type [4]int, you can't use that function to take and process an array of type [5]int), but this may also be an advantage: this may be used to explicitly specify the length of the array that is expected. E.g. you want to write a function which takes an IPv4 address, it can be modeled with the type [4]byte. Now you have a compile-time guarantee that the value passed to your function will have exactly 4 bytes, no more and no less (which would be an invalid IPv4 address anyway).
5. Related to the previous, the array length may also serve a documentation purpose. A type [4]byte properly documents that IPv4 has 4 bytes. An rgb variable of type [3]byte tells there are 1 byte for each color components. In some cases it is even taken out and is available, documented separately; for example in the crypto/md5 package: md5.Sum() returns a value of type [Size]byte where md5.Size is a constant being 16: the length of an MD5 checksum.
6. They are also very useful when planning memory layout of struct types, see JimB's answer here, and this answer in greater detail and real-life example.
7. Also since slices are headers and they are (almost) always passed around as-is (without pointers), the language spec is more restrictive regarding pointers to slices than pointers to arrays. For example the spec provides multiple shorthands for operating with pointers to arrays, while the same gives compile-time error in case of slices (because it's rare to use pointers to slices, if you still want / have to do it, you have to be explicit about handling it; read more in this answer).
Such examples are:
Slicing a p pointer to array: p[low:high] is a shorthand for (*p)[low:high]. If p is a pointer to slice, this is compile-time error (spec: Slice expressions).
Indexing a p pointer to array: p[i] is a shorthand for (*p)[i]. If p is pointer to a slice, this is a compile time error (spec: Index expressions).
Example:
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. Accessing (single) array elements is more efficient than accessing slice elements; as in case of slices the runtime has to go through an implicit pointer dereference. Also "the expressions len(s) and cap(s) are constants if the type of s is an array or pointer to an array".
May be suprising, but you can even write:
type IP [4]byte const x = len(IP{}) // x will be 4
It's valid, and is evaluated and compile-time even though IP{} is not a constant expression so e.g. const i = IP{} would be a compile-time error! After this, it's not even surprising that the following also works:
const x2 = len((*IP)(nil)) // x2 will also be 4
See related questions where an array can be used / makes more sense than a slice:
Why use arrays instead of slices?
Why can't Go slice be used as keys in Go maps pretty much the same way arrays can be used as keys?
Hash with key as an array type
Golang: How do I check the equality of three values elegantly?
Slicing a slice pointer passed as argument
And this is just for curiosity: a slice can contain itself while an array can't. (Actually this property makes comparison easier as you don't have to deal with recursive data structures).
Must-read blogs:
Go Slices: usage and internals
Arrays, slices (and strings): The mechanics of 'append'
这篇关于为什么在Go中有数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!