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

切片别名。

切片别名。

Go
收到一只叮咚 2023-05-08 15:58:51
如何检查两个切片是否被同一个数组备份?例如:a := []int{1, 2, 3} b := a[0:1] c := a[2:3] alias(b, c) == true应该是什么alias样子?
查看完整描述

2 回答

?
杨__羊羊

TA贡献1943条经验 获得超7个赞

一般来说,你无法判断 backing array 是否在 2 个切片之间共享,因为使用完整的切片表达式,可能会控制结果切片的容量,然后即使在检查容量时也不会有重叠。

例如,如果您有一个包含 10 个元素的后备数组,则可能会创建一个仅包含前 2 个元素的切片,其容量可能为 2。并且可能会创建另一个仅包含其最后 2 个元素的切片,其容量再次成为2。

看这个例子:

a := [10]int{}

x := a[0:2:2]
y := a[8:10:10]

fmt.Println("len(x) = ", len(x), ", cap(x) = ", cap(x))
fmt.Println("len(y) = ", len(y), ", cap(y) = ", cap(y))


上面的代码将打印出 和 的长度和容量x均为y2。它们显然具有相同的后备数组,但您没有任何方法可以说明这一点。


编辑:我误解了这个问题,下面描述了如何判断 2 个切片(的元素)是否重叠。

对此没有语言支持,但由于切片有一些后备数组的连续部分,我们可以检查它们元素的地址范围是否重叠。

不幸的是,从我们不能对它们应用<and>运算符的意义上讲,指针是无序的(Go 中有指针,但没有指针算法)。并检查第一个切片元素的所有地址是否与第二个切片中的任何元素匹配,这是不可行的。

但是我们可以获得一个指针值(地址)作为uintptr使用反射包的一种类型,更具体地说是Value.Pointer()方法(或者我们也可以使用包来做到这一点unsafe,但reflect“更安全”),并且uintptr值是整数,它们是有序的,所以我们可以比较它们。

那么我们可以做的就是获取切片的第一个和最后一个元素的地址,通过比较它们,我们可以判断它们是否重叠。

这是一个简单的实现:

func overlap(a, b []int) bool {

    if len(a) == 0 || len(b) == 0 {

        return false

    }


    amin := reflect.ValueOf(&a[0]).Pointer()

    amax := reflect.ValueOf(&a[len(a)-1]).Pointer()

    bmin := reflect.ValueOf(&b[0]).Pointer()

    bmax := reflect.ValueOf(&b[len(b)-1]).Pointer()


    return !(amax < bmin || amin > bmax)

}

测试它:


a := []int{0, 1, 2, 3}

b := a[0:2]

c := a[2:4]

d := a[0:3]


fmt.Println(overlap(a, b)) // true

fmt.Println(overlap(b, c)) // false

fmt.Println(overlap(c, d)) // true

在Go Playground上尝试一下。



查看完整回答
反对 回复 2023-05-08
?
阿晨1998

TA贡献2037条经验 获得超6个赞

这个想法是,虽然我不认为有一种方法可以找到支持数组的开头,但切片的ptr + cap应该 [*] 指向它的结尾。因此,然后比较最后一个指针是否相等,例如:

func alias(x, y nat) bool { 
   return cap(x) > 0 && cap(y) > 0 && &x[0:cap(x)][cap(x)-1] == &y[0:cap(y)][cap(y)-1]
}

[*] 该代码包括以下注释:

注意:别名假定底层数组的容量永远不会因 nat 值而改变;也就是说,这段代码中没有 3 操作数切片表达式(或者更糟的是,基于反射的操作具有相同的效果)。


查看完整回答
反对 回复 2023-05-08
  • 2 回答
  • 0 关注
  • 112 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信