切片别名

时间:2018-11-13 11:47:15

标签: arrays pointers go memory slice

如何检查两个切片是否由同一阵列备份?

例如:

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

alias(b, c) == true

alias应该是什么样?

2 个答案:

答案 0 :(得分:3)

通常,您无法确定两个切片之间是否共享了后备数组,因为使用full slice expression可以控制所得切片的容量,因此即使检查容量。

例如,如果您有一个包含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))

上面的代码将打印xy的长度和容量均为2。它们显然具有相同的后备数组,但是您没有任何办法告诉您。


编辑:我误解了这个问题,以下内容描述了如何确定2个切片的(元素)是否重叠。

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

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

但是我们可以使用反射包,更具体地说是Value.Pointer()方法(或者也可以使用包{{1}来实现),将指针值(地址)作为uintptr类型的指针。 },但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)
}

Go Playground上尝试。

答案 1 :(得分:0)

找到了此here的一种方式。我的想法是,尽管我不认为有一种方法可以找到后备数组的开始,但切片的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操作数切片表达式(或更糟糕的是,基于反射的操作具有相同的效果)。