我正在尝试使用“Go Go Programming Language”来学习Golang,并且我已经达到了关于切片的部分。他们在数组和切片之间进行比较,因为两个数组可以与两个切片不能的==
进行比较。案文内容如下:
"== operator for arrays of strings, it may be puzzling that slice
comparisons do not also work this way. There are two reasons why deep
equivalence is problematic. First, unlike array elements, the elements
of a slice are indirect, making it possible for a slice to contain
itself. Although there are ways to deal with such cases, none is
simple, efficient, and most importantly, obvious."
由于元素是间接的,因此切片可能包含自身是什么意思?
答案 0 :(得分:11)
除了递归类型(例如type Foo []Foo
,请参见ANisus的答案),除了演示之外什么也没有用,如果例如切片的元素类型是{{1 }}:
interface{}
在此示例中,切片s := []interface{}{"one", nil}
s[1] = s
将具有2个接口值,第一个"包装"一个简单的字符串s
,以及另一个包含切片值本身的接口值。创建接口值时,将包装该值的副本,在切片的情况下,它表示切片头/描述符的副本,其中包含指向底层数组的指针,因此副本将具有指向相同的指针值相同的底层数组。 (有关接口表示的更多详细信息,请参阅The Laws of Reflection: The representation of an interface。)
如果你很快就打印出来了:
"one"
你会得到一个致命的错误,例如:
fmt.Println(s)
因为runtime: goroutine stack exceeds 250000000-byte limit
fatal error: stack overflow
尝试以递归方式打印内容,并且由于第二个元素是指向正在打印的切片的同一数组的切片,因此它会进入无限循环。
另一种看待它本身就是切片的方法:
fmt.Println()
输出(在Go Playground上尝试):
s := []interface{}{"one", nil}
s[1] = s
fmt.Println(s[0])
s2 := s[1].([]interface{})
fmt.Println(s2[0])
s3 := s2[1].([]interface{})
fmt.Println(s3[0])
无论我们走多远,第二个元素将始终是指向与one
one
one
相同的数组的切片值,包含在s
值中。
间接起着重要的作用,因为副本将包含在interface{}
中,但副本将包含相同的指针。
将类型更改为数组:
interface{}
输出(在Go Playground上尝试):
s := [2]interface{}{"one", nil}
s[1] = s
fmt.Println(s[0])
s2 := s[1].([2]interface{})
fmt.Println(s2[0])
s3 := s2[1].([2]interface{})
fmt.Println(s3[0])
这是因为当数组被包装到one
one
panic: interface conversion: interface is nil, not [2]interface {}
中时,副本将被包装 - 并且副本不是原始数组。所以interface{}
将有第二个值,s
包装一个数组,但这是一个不同的数组,其第二个值未设置,因此将是interface{}
(类型{的零值{1}}),所以试图进入"这个数组会因为nil
(type assertion失败而引起恐慌,因为没有使用特殊的"逗号,ok和#34;表格。
由于此interface{}
数组不包含自身,因此简单的nil
将显示其完整内容:
s
输出:
fmt.Println()
fmt.Println(s)
包装分析如果您将数组包装在[one [one <nil>]]
中并修改原始数组的内容,则interface{}
中包含的值不会受到影响:
interface{}
输出:
interface{}
如果对切片执行相同操作,则包装切片(因为指向相同的基础数组)也会受到影响:
arr := [2]int{1, 2}
var f interface{} = arr
arr[0] = 11
fmt.Println("Original array: ", arr)
fmt.Println("Array in interface:", f)
输出:
Original array: [11 2]
Array in interface: [1 2]
在Go Playground上尝试这些。
答案 1 :(得分:4)
以下示例创建一个包含自身的切片:
type Foo []Foo
bar := make(Foo, 1)
bar[0] = bar
这可以做到,因为切片值内部包含指向数组的指针,长度和容量。
另一方面,数组是一个值。它充其量只能包含指向自身的指针。
答案 2 :(得分:0)
切片包含指向保存元素的内存的指针,可用元素计数的长度以及内存大小的能力。所以它像:
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
我认为它是indirect
,因为这些元素是由指针引用的。
当然,我们可以将切片本身放在void *data
。