Dictionary 为什么切片值有时会过时,但从不映射值?
我发现切片映射函数和通道经常作为引用类型一起提到。然而,我注意到有些东西表现出非引用行为,就像它们会过时一样:Dictionary 为什么切片值有时会过时,但从不映射值?,dictionary,pointers,go,reference,slice,Dictionary,Pointers,Go,Reference,Slice,我发现切片映射函数和通道经常作为引用类型一起提到。然而,我注意到有些东西表现出非引用行为,就像它们会过时一样: var s []int //must update slice value s = append(s, ...) 或 通常我是通过记住slice描述器实现的内部组件来理解这一点的:slice值可以被看作len、cap和数据指针的结构 但地图值永远不需要像这样麻烦 m := make(map[string]int) ... // don't kno
var s []int
//must update slice value
s = append(s, ...)
或
通常我是通过记住slice描述器实现的内部组件来理解这一点的:slice值可以被看作len、cap和数据指针的结构
但地图值永远不需要像这样麻烦
m := make(map[string]int)
...
// don't know how to express with insertion, but you know what i mean.
m = delete(m, "well")
为什么??映射值只是映射描述符的指针吗?如果是这样的话,为什么不这样做呢?slice是连续内存块上的薄纸包装,通常可以部分或全部重用该内容(避免复制数据)。地图没有这些特征。它是一个复杂的数据结构,行为复杂,不能重用它的存储(就像你用C++那样)。在围棋中,一切都是按价值传递的。在Go中使用术语“reference type”时,它表示引用它们应该表示的数据的类型(通过指针) 切片是由以下类型表示的类似结构的小数据结构: 它包含一个指针,指向底层数组中切片的第一个元素(
SliceHeader.Data
field)。该结构很小,作为值传递很有效,无需传递其地址(并取消引用以间接访问其任何字段)。切片的元素不存储在切片标头中,而是存储在标头内存区域之外的数组中。这意味着修改“定点”元素将修改原始切片的元素
当您将(超过0)个元素附加到切片时,标头中的Len
字段必须更改,因此使用附加元素描述切片的新切片必须与附加之前的切片不同,这就是为什么您需要指定内置append()
函数的返回值。(其他值也可能会更改,但Len
sure必须更改。)
映射被实现为指向运行时.hmap
结构的指针:
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
正如您所看到的,这是一个比切片头复杂得多的数据结构,而且要大得多,将其作为值传递是没有效率的
从映射中添加/删除元素(键值对)存储在由此结构的字段引用的存储桶中,但由于映射在后台作为指针处理,因此不需要分配此类操作的结果
完整地说,通道也被实现为指针,指向运行时
包的hchan
类型:
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}
这也是一个“fat”结构,处理方式类似于映射值
参见相关问题:
对于初学者来说,将(非指针)切片传递到函数中最令人困惑的事情之一是:函数可以更改切片中的元素,但不能更改容量或长度。因此,如果切片具有容量,则追加可能会起作用,但如果没有,并且切片被重新分配到封盖下,则此更改将丢失,因为复制的切片标头发生了更改。解决方案:传递片地址。@colminator或返回新片,就像内置的
append()
一样,并让调用者分配它。这非常详细,谢谢@icza。顺便说一句,我注意到我的问题被否决了,这是否意味着我的思考或描述方式不是通常的gopher方式,并且可能受到其他编程语言的影响,这不是一个好的开始?
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
type hchan struct {
qcount uint // total data in the queue
dataqsiz uint // size of the circular queue
buf unsafe.Pointer // points to an array of dataqsiz elements
elemsize uint16
closed uint32
elemtype *_type // element type
sendx uint // send index
recvx uint // receive index
recvq waitq // list of recv waiters
sendq waitq // list of send waiters
// lock protects all fields in hchan, as well as several
// fields in sudogs blocked on this channel.
//
// Do not change another G's status while holding this lock
// (in particular, do not ready a G), as this can deadlock
// with stack shrinking.
lock mutex
}