首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >切片如何包含其自身?

切片如何包含其自身?
EN

Stack Overflow用户
提问于 2016-03-18 14:26:09
回答 3查看 1.3K关注 0票数 14

我正在尝试使用“Go编程语言”学习Golang,我已经读到了切片部分。他们在数组和切片之间进行比较,因为两个数组可以与==进行比较,而两个切片不能。正文内容如下:

代码语言:javascript
运行
复制
"== 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."

由于元素是间接的,切片可以包含自己是什么意思?

EN

回答 3

Stack Overflow用户

发布于 2016-03-18 15:55:43

包含自身的切片

除了递归类型(如type Foo []Foo,请参阅ANisus的答案)之外,除了演示之外,如果切片的元素类型为interface{},则切片可以包含其自身

代码语言:javascript
运行
复制
s := []interface{}{"one", nil}
s[1] = s

在本例中,切片s将有两个接口值,第一个“包装”简单字符串"one",另一个接口值包装切片值本身。当创建一个接口值时,该值的副本将被包装,在切片的情况下,这意味着包含指向底层数组的指针的切片头部/描述符的副本,因此该副本将具有指向相同底层数组的相同指针值。(有关接口表示的更多详细信息,请参阅The Laws of Reflection: The representation of an interface。)

如果你很快就把它打印出来:

代码语言:javascript
运行
复制
fmt.Println(s)

你会得到一个致命的错误,类似于:

代码语言:javascript
运行
复制
runtime: goroutine stack exceeds 250000000-byte limit
fatal error: stack overflow

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

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

代码语言:javascript
运行
复制
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上试用):

代码语言:javascript
运行
复制
one
one
one

无论我们深入到什么程度,第二个元素总是指向与s相同的数组的切片值,包装在一个interface{}值中。

间接性扮演着重要的角色,因为副本将被包装在interface{}中,但副本将包含相同的指针。

数组不能包含自身

将类型更改为数组:

代码语言:javascript
运行
复制
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上试用):

代码语言:javascript
运行
复制
one
one
panic: interface conversion: interface is nil, not [2]interface {}

这是因为当数组被包装到interface{}中时,将包装一个副本-而副本不是原始数组。所以s将有第二个值,一个包装数组的interface{},但是这是一个不同的数组,它的第二个值没有设置,因此将是nil (interface{}类型的零值),所以尝试“进入”这个数组将会死机,因为它是nil (type assertion失败,因为没有使用特殊的“逗号,ok”形式)。

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

代码语言:javascript
运行
复制
fmt.Println(s)

输出:

代码语言:javascript
运行
复制
[one [one <nil>]]

进一步的interface{}包装分析

如果将数组包装在interface{}中并修改原始数组的内容,则包装在interface{}中的值不受影响:

代码语言:javascript
运行
复制
arr := [2]int{1, 2}
var f interface{} = arr
arr[0] = 11

fmt.Println("Original array:    ", arr)
fmt.Println("Array in interface:", f)

输出:

代码语言:javascript
运行
复制
Original array:     [11 2]
Array in interface: [1 2]

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

代码语言:javascript
运行
复制
s := []int{1, 2}
f = s
s[0] = 11

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

输出:

代码语言:javascript
运行
复制
Original slice:     [11 2]
Slice in interface: [11 2]

Go Playground上试试这些。

票数 19
EN

Stack Overflow用户

发布于 2016-03-18 15:00:35

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

代码语言:javascript
运行
复制
type Foo []Foo  
bar := make(Foo, 1)
bar[0] = bar

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

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

票数 7
EN

Stack Overflow用户

发布于 2016-03-18 15:56:37

切片包含指向保存元素的内存的指针、可用元素计数的长度以及内存大小的容量。所以它是这样的:

代码语言:javascript
运行
复制
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

我认为它是indirect,因为元素是由指针引用的。当然,我们可以在void *data中拥有切片本身。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36077566

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档