为了账号安全,请及时绑定邮箱和手机立即绑定

切片如何包含自身?

切片如何包含自身?

Go
繁花如伊 2021-12-27 10:42:31
我正在尝试使用“Go 编程语言”来学习 Golang,并且我已经到达了切片部分。他们在数组和切片之间进行比较,因为两个数组可以比较,==而两个切片不能比较。正文如下:"== operator for arrays of strings, it may be puzzling that slicecomparisons do not also work this way. There are two reasons why deep equivalence is problematic. First, unlike array elements, the elementsof 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."由于元素是间接的,切片可能包含自身是什么意思?
查看完整描述

3 回答

?
慕的地8271018

TA贡献1796条经验 获得超4个赞

包含自身的切片

除了递归类型(例如type Foo []Foo,参见 ANisus 的回答),它除了演示之外一无是处,如果例如切片的元素类型是interface{}:


s := []interface{}{"one", nil}

s[1] = s

在这个例子中,切片s将有 2 个接口值,第一个“包装”一个简单的 string "one",另一个接口值包装切片值本身。创建接口值时,将包装该值的副本,在切片的情况下,这意味着切片头/描述符的副本,其中包含指向底层数组的指针,因此副本将具有相同的指针值指向相同的底层数组。(有关接口表示的更多详细信息,请参阅反射定律:接口的表示。)


如果您很快就可以打印它:


fmt.Println(s)

你会得到一个致命的错误,比如:


runtime: goroutine stack exceeds 250000000-byte limit

fatal error: stack overflow

因为fmt.Println()尝试递归打印内容,并且由于第二个元素是指向正在打印的切片的同一数组的切片,所以它会陷入无限循环。


查看它是否真的是切片本身的另一种方法:


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

输出(在Go Playground上试试):


one

one

one

无论我们走多深,第二个元素始终是指向与 相同数组的切片值s,包裹在一个interface{}值中。


间接性起着重要的作用,因为interface{}副本将包含在 中,但副本将包含相同的指针。


数组不能包含自身

将类型更改为数组:


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

输出(在Go Playground上试试):


one

one

panic: interface conversion: interface is nil, not [2]interface {}

这是因为当数组被包装成一个时interface{},一个副本将被包装 - 并且副本不是原始数组。所以s会有第二个值,一个interface{}包裹数组,但这是一个不同的数组,它的第二个值没有设置,因此将是nil( type 的零值interface{}),所以试图“进入”这个数组会恐慌,因为它是nil(类型断言失败,因为没有使用特殊的“逗号,确定”形式)。


由于这个s数组不包含自身,一个简单的fmt.Println()将显示其全部内容:


fmt.Println(s)

输出:


[one [one <nil>]]

进一步的interface{}包装分析

如果将数组包裹在 an 中interface{}并修改原始数组的内容,则包裹在 the 中的值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]

如果对切片执行相同操作,则包装的切片(因为指向相同的底层数组)也会受到影响:


s := []int{1, 2}

f = s

s[0] = 11


fmt.Println("Original slice:    ", s)

fmt.Println("Slice in interface:", f)

输出:


Original slice:     [11 2]

Slice in interface: [11 2]

在Go Playground上试试这些。


查看完整回答
反对 回复 2021-12-27
?
凤凰求蛊

TA贡献1825条经验 获得超4个赞

下面的示例创建一个包含自身的切片:


type Foo []Foo  

bar := make(Foo, 1)

bar[0] = bar

这是可以做到的,因为切片值内部包含一个指向数组的指针、长度和容量。


另一方面,数组是一个值。它最多可以包含指向自身的指针。


查看完整回答
反对 回复 2021-12-27
?
慕神8447489

TA贡献1780条经验 获得超1个赞

切片包含指向保存元素的内存的指针、可用元素计数的长度以及内存大小的能力。所以它喜欢:

typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

我认为是indirect,因为元素是由指针引用的。当然,我们可以将切片本身放入void *data.


查看完整回答
反对 回复 2021-12-27
  • 3 回答
  • 0 关注
  • 262 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号