一个空的切片为什么会占用24个字节?

huangapple go评论93阅读模式
英文:

Why does an empty slice have 24 bytes?

问题

我想了解当创建一个空切片make([]int, 0)时会发生什么。我为了测试而编写了以下代码:

emptySlice := make([]int, 0)
fmt.Println(len(emptySlice))
fmt.Println(cap(emptySlice))
fmt.Println(unsafe.Sizeof(emptySlice))

显然,长度和容量都是0,但切片的大小是24字节,为什么呢?

24字节应该是3个int64对吧?一个具有24字节的切片的内部数组应该是类似[3]int{},那么为什么一个空切片占用24字节呢?

英文:

I'm want to understand what happens when created an empty slice with make([]int, 0). I do this code for test:

emptySlice := make([]int, 0)
fmt.Println(len(emptySlice))
fmt.Println(cap(emptySlice))
fmt.Println(unsafe.Sizeof(emptySlice))

The size and capacity return is obvious, both are 0, but the size of slice is 24 bytes, why?

24 bytes should be 3 int64 right? One internal array for a slice with 24 bytes should be something like: [3]int{}, then why one empty slice have 24 bytes?

答案1

得分: 12

如果你阅读了unsafe.Sizeof文档,它解释了这里发生了什么:

大小不包括x可能引用的任何内存。例如,如果x是一个切片,Sizeof返回的是切片描述符的大小,而不是切片引用的内存的大小。

Go中的所有数据类型都是静态大小的。即使切片具有动态数量的元素,这也不能反映在数据类型的大小上,因为那样就不是静态的了。

"切片描述符",顾名思义,是描述切片的所有数据。这实际上是存储在切片变量中的内容。

Go中的切片有3个属性:底层数组(内存地址),切片的长度(内存偏移量)和切片的容量(内存偏移量)。在64位应用程序中,内存地址和偏移量往往以64位(8字节)的值存储。这就是为什么你看到大小为24(= 3 * 8)字节的原因。

英文:

If you read the documentation for unsafe.Sizeof, it explains what's going on here:

> The size does not include any memory possibly referenced by x. For instance, if x is a slice, Sizeof returns the size of the slice descriptor, not the size of the memory referenced by the slice.

All data types in Go are statically sized. Even though slices have a dynamic number of elements, that cannot be reflected in the size of the data type, because then it would not be static.

The "slice descriptor", as implied by the name, is all the data that describes a slice. That is what is actually stored in a slice variable.

Slices in Go have 3 attributes: The underlying array (memory address), the length of the slice (memory offset), and the capacity of the slice (memory offset). In a 64-bit application, memory addresses and offsets tend to be stored in 64-bit (8-byte) values. That is why you see a size of 24 (= 3 * 8 ) bytes.

答案2

得分: 6

unsafe.Sizeof是指对象在内存中的大小,与C和C++中的sizeof完全相同。请参阅https://stackoverflow.com/q/26975738/995714

切片具有大小,但也具有调整大小的能力,因此最大的调整大小能力也必须存储在某个地方。但是,可调整大小也意味着它不能是静态数组,而是需要存储指向其他(可能是动态分配的)数组的指针。

整个过程意味着它需要存储其***{开始,结束,最后一个有效索引}{开始,大小,容量}***。这是一个由3个值组成的元组,这意味着在64位平台上,它的内存表示至少是3×8字节,除非您希望将最大大小和容量限制得比264字节小得多。

在许多具有相同动态大小能力的C++类型中,情况完全相同,例如std::stringstd::vector也是一个24字节的类型,尽管在某些实现中,为了对齐原因,会添加8字节的填充,从而得到一个32字节的字符串类型。请参阅

实际上,Go语言的strings.Builder与C++的std::string最接近,其大小为32字节。请参见demo

英文:

unsafe.Sizeof is the size of the object in memory, exactly the same as sizeof in C and C++. See https://stackoverflow.com/q/26975738/995714

A slice has size, but also has the ability to resize, so the maximum resizing ability must also be stored somewhere. But being resizable also means that it can't be a static array but needs to store a pointer to some other (possibly dynamically allocated) array

The whole thing means it needs to store its { begin, end, last valid index } or { begin, size, capacity }. That's a tuple of 3 values which means its in-memory representation is at least 3×8 bytes on 64-bit platforms, unless you want to limit the maximum size and capacity to much smaller than 2<sup>64</sup> bytes

It's exactly the same situation in many C++ types with the same dynamic sizing capability like std::string or std::vector is also a 24-byte type although on some implementations 8 bytes of padding is added for alignment reasons, resulting in a 32-byte string type. See

In fact golang's strings.Builder which is the closest to C++'s std::string has size of 32 bytes. See demo

huangapple
  • 本文由 发表于 2021年6月4日 23:15:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/67839752.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定