为什么切片值有时会过时但不能映射值?
我发现切片图功能和通道经常作为引用类型一起提及。但是,我注意到,某些切片没有表现出过时的参考行为:
I find that slice map function and channel are frequently mentioned together as reference types. However I notice that slices somethings exhibit none reference behavior like they can go stale:
var s []int
//must update slice value
s = append(s, ...)
或
//must use pointer if we want to expose the change
func foo(s *[]int) error
//or change the function signature to return it like _append_
func foo(s []int) (r slice, err error)
通常,我会通过记住slice描述符实现的内部组件来理解这一点:slice值可以看作len,cap和data指针的结构。
Usually I understand this by keeping the inner components of the slice discriptor implementation in mind: A slice value can be seen as a struct of len, cap and data pointer.
但是地图值永远不需要麻烦
But map values need never bother like
m := make(map[string]int)
...
// don't know how to express with insertion, but you know what i mean.
m = delete(m, "well")
为什么?映射值仅仅是指向映射描述符的指针吗?如果是这样,为什么还不这样做呢?
Why? Is a map value just a pointer to the map descriptor? If so why not also make slice this way?
在Go中,没有像C ++中那样的引用类型。在Go中,一切都是通过价值传递的。在Go中使用引用类型一词时,它表示引用它们应表示的数据(通过指针)的类型。
In Go there are no reference types like you have them in C++. In Go everything is passed by value. When the term "reference type" is used in Go, it means a type that references to the data they ought to represent (via pointers).
切片很小,结构类型的数据结构,由类型 reflect.SliceHeader
表示> :
Slices are small, struct-like data structures represented by the type reflect.SliceHeader
:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
它包含指向基础中切片的第一个元素的指针数组( SliceHeader.Data
字段)。该结构很小,可以作为值有效传递,而无需传递其地址(并取消引用以间接访问其任何字段)。切片的元素未存储在切片标头中,而是存储在标头的存储区域之外的数组中。这意味着修改指向元素将修改原始切片的元素。
It contains a pointer to the first element of the slice in an underlying array (SliceHeader.Data
field). This struct is small and is efficient to pass as a value, no need to pass its address (and dereference it to access indirectly any of its fields). The elements of a slice are not stored in the slice header, but in an array outside of the header's memory area. This means modifying a "pointed" element will modify the element of the original slice.
当您将(大于0)元素附加到切片时, Len 字段必须更改,因此描述带有附加元素的切片的新切片必须不同于追加之前的切片,这就是为什么您需要分配的返回值的原因内置的 append()
函数。 (其他值也可能会更改,但是 Len
当然必须更改。)
When you append (more than 0) elements to a slice, the Len
field in the header must change, so the new slice that describes the slice with the additional elements must be a different than the one before the append, that's why you need to assign the return value of the builtin append()
function. (Other values may also change, but Len
sure must change.)
映射被实现为指向 runtime.hmap
结构:
Maps are implemented as pointers to the runtime.hmap
structure:
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
}
如您所见,
添加/删除元素(键值)映射中的对)存储在此struct的字段所引用的存储桶中,但是由于映射在后台作为指针处理,因此您无需分配此类操作的结果。
Adding / removing elements (key-value pairs) from a map is stored in buckets that are referenced by the fields of this struct, but since maps are handled as pointers under the hood, you do not need to assign the result of such operations.
为了完整起见,通道也被实现为指针,指向运行时
包的 hchan
类型:
To be complete, channels are also implemented as pointers, pointing to the runtime
package's hchan
type:
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
}
这又是一个胖结构,
查看相关问题: