Go不允许使用地图成员的地址:
// if I do this: p := &mm["abc"] // Syntax Error - cannot take the address of mm["abc"]
理由是,如果Go允许使用此地址,则当地图后备存储增长或减少时,该地址可能变得无效,从而使用户感到困惑。
但是,当Go slice超出其容量时,它将被重新定位,但是Go允许我们获取slice元素的地址:
a := make([]Test, 5) a[0] = Test{1, "dsfds"} a[1] = Test{2, "sdfd"} a[2] = Test{3, "dsf"} addr1 := reflect.ValueOf(&a[2]).Pointer() fmt.Println("Address of a[2]: ", addr1) a = append(a, Test{4, "ssdf"}) addrx := reflect.ValueOf(&a[2]).Pointer() fmt.Println("Address of a[2] After Append:", addrx) // Note after append, the first address is invalid Address of a[2]: 833358258224 Address of a[2] After Append: 833358266416
为什么Go这样设计?切片元素的地址有何特殊之处?
切片和贴图之间的主要区别在于:切片由后备数组支持,而贴图则没有。
如果地图增大或缩小,则指向地图元素的潜在指针可能会变成指向任何地方(未初始化的内存)的悬空指针。这里的问题不是“用户困惑”,而是它会破坏Go的主要设计元素:没有悬空的指针。
如果切片用完了容量,则会创建一个新的更大的后备阵列,并将旧的后备阵列复制到新的后备阵列中。并且旧的支持阵列 仍然 存在 。因此,从指向原始支持数组的“未成年人”切片中获得的任何指针仍然是指向有效内存的有效指针。
如果您有一个切片仍指向旧的支持阵列(例如,因为在将切片增长到超出其容量之前已复制了该切片),您仍将访问旧的支持阵列。这与切片元素的指针关系不大,但是切片是视图到数组中,并且数组在切片增长期间被复制。
注意,在切片收缩期间没有“减少切片的衬背阵列”。