在Go中获取结构体的大小

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

Sizeof struct in Go

问题

我正在研究Go语言,它看起来非常有前途。
我正在尝试弄清楚如何获取go结构体的大小,例如像这样的结构体:

type Coord3d struct {
    X, Y, Z int64
}

当然,我知道它占用24个字节,但我想以编程的方式知道它的大小。

你有任何想法如何做到这一点吗?

英文:

I'm having a look at Go, which looks quite promising.
I am trying to figure out how to get the size of a go struct, for
example something like

type Coord3d struct {
    X, Y, Z int64
}

Of course I know that it's 24 bytes, but I'd like to know it programmatically..

Do you have any ideas how to do this ?

答案1

得分: 50

Roger已经展示了如何使用unsafe包中的SizeOf方法。在依赖该函数返回值之前,请确保您已阅读了此内容:

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

除此之外,我还想解释一下如何使用几个简单的规则轻松计算任何结构体的大小。然后,如何使用一个有用的服务来验证您的直觉。


结构体的大小取决于它所包含的类型和结构体字段的顺序(因为会使用不同的填充)。这意味着具有相同字段的两个结构体可能具有不同的大小。

例如,这个结构体的大小为32

struct {
    a bool
    b string
    c bool
}

稍作修改后,它的大小为24(仅仅由于字段更紧凑的排序而导致的**25%**差异):

struct {
    a bool
    c bool
    b string
}

在Go中获取结构体的大小 在Go中获取结构体的大小

从图片中可以看出,在第二个示例中,我们移除了一个填充,并移动了一个字段以利用之前的填充。对齐方式可以是1、2、4或8。填充是用来填充变量以满足对齐方式的空间(基本上是浪费的空间)。

了解了这个规则并记住:

  • bool、int8/uint8 占用1字节
  • int16、uint16 占用2字节
  • int32、uint32、float32 占用4字节
  • int64、uint64、float64、指针 占用8字节
  • string 占用16字节(2个8字节的对齐方式)
  • 任何切片占用24字节(3个8字节的对齐方式)。所以[]bool[][][]string是相同的(不要忘记重新阅读我在开头添加的引用)
  • 长度为n的数组占用n * 它所占用的类型的字节。

掌握了填充、对齐和字节大小的知识,您可以快速找出如何改进您的结构体(但仍然有必要使用服务来验证您的直觉)。

英文:

Roger already showed how to use SizeOf method from the unsafe package. Make sure you read this before relying on the value returned by the function:

> 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.

In addition to this I wanted to explain how you can easily calculate the size of any struct using a couple of simple rules. And then how to verify your intuition using a helpful service.


The size depends on the types it consists of and the order of the fields in the struct (because different padding will be used). This means that two structs with the same fields can have different size.

For example this struct will have a size of 32

struct {
    a bool
    b string
    c bool
}

and a slight modification will have a size of 24 (a 25% difference just due to a more compact ordering of fields)

struct {
    a bool
    c bool
    b string
}

在Go中获取结构体的大小 在Go中获取结构体的大小

As you see from the pictures, in the second example we removed one of the paddings and moved a field to take advantage of the previous padding. An alignment can be 1, 2, 4, or 8. A padding is the space that was used to fill in the variable to fill the alignment (basically wasted space).

Knowing this rule and remembering that:

  • bool, int8/uint8 take 1 byte
  • int16, uint16 - 2 bytes
  • int32, uint32, float32 - 4 bytes
  • int64, uint64, float64, pointer - 8 bytes
  • string - 16 bytes (2 alignments of 8 bytes)
  • any slice takes 24 bytes (3 alignments of 8 bytes). So []bool, [][][]string are the same (do not forget to reread the citation I added in the beginning)
  • array of length n takes n * type it takes of bytes.

Armed with the knowledge of padding, alignment and sizes in bytes, you can quickly figure out how to improve your struct (but still it makes sense to verify your intuition using the service).

答案2

得分: 47

import unsafe "unsafe"

/* 描述inotify事件的结构体 */
type INotifyInfo struct {
Wd int32 // 监听描述符
Mask uint32 // 监听掩码
Cookie uint32 // 用于同步两个事件的Cookie
Len uint32 // 名称的长度(包括NULs)
}

func doSomething() {
var info INotifyInfo
const infoSize = unsafe.Sizeof(info)
...
}

英文:
import unsafe "unsafe"

/* Structure describing an inotify event.  */
type INotifyInfo struct {
    Wd     int32  // Watch descriptor
    Mask   uint32 // Watch mask
    Cookie uint32 // Cookie to synchronize two events
    Len    uint32 // Length (including NULs) of name
}

func doSomething() {
    var info INotifyInfo
    const infoSize = unsafe.Sizeof(info)
    ...
}

NOTE: The OP is mistaken. The unsafe.Sizeof does return 24 on the example Coord3d struct. See comment below.

答案3

得分: 10

binary.TotalSize也是一个选项,但请注意它与unsafe.Sizeof之间的行为差异:binary.TotalSize包括切片内容的大小,而unsafe.Sizeof仅返回顶级描述符的大小。以下是如何使用TotalSize的示例。

package main

import (
    "encoding/binary"
    "fmt"
    "reflect"
)

type T struct {
    a uint32
    b int8
}

func main() {
    var t T
    r := reflect.ValueOf(t)
    s := binary.TotalSize(r)

    fmt.Println(s)
}
英文:

binary.TotalSize is also an option, but note there's a slight difference in behavior between that and unsafe.Sizeof: binary.TotalSize includes the size of the contents of slices, while unsafe.Sizeof only returns the size of the top level descriptor. Here's an example of how to use TotalSize.

package main

import (
	"encoding/binary"
	"fmt"
	"reflect"
)

type T struct {
	a uint32
	b int8
}

func main() {
	var t T
	r := reflect.ValueOf(t)
	s := binary.TotalSize(r)

	fmt.Println(s)
}

答案4

得分: 1

这是可能会改变的,但是我最后查看时存在一个与结构对齐相关的编译器错误(bug260.go)。最终结果是,对结构进行打包可能不会得到预期的结果。这是针对编译器6g版本5383 release.2010-04-27 release的情况。这可能不会影响您的结果,但这是需要注意的事项。

更新:截至2010-05-04发布,go测试套件中唯一剩下的错误是上述提到的bug260.go。

Hotei

英文:

This is subject to change but last I looked there is an outstanding compiler bug (bug260.go) related to structure alignment. The end result is that packing a structure might not give the expected results. That was for compiler 6g version 5383 release.2010-04-27 release. It may not be affecting your results, but it's something to be aware of.

UPDATE: The only bug left in go test suite is bug260.go, mentioned above, as of release 2010-05-04.

Hotei

答案5

得分: 1

返回任何类型对象的字节大小

func getRealSizeOf(v interface{}) (int, error) {
b := new(bytes.Buffer)
if err := gob.NewEncoder(b).Encode(v); err != nil {
return 0, err
}
return b.Len(), nil
}

英文:
/*
	returns the size of any type of object in bytes
*/

func getRealSizeOf(v interface{}) (int, error) {
	b := new(bytes.Buffer)
	if err := gob.NewEncoder(b).Encode(v); err != nil {
		return 0, err
	}
	return b.Len(), nil
}

答案6

得分: 0

为了避免初始化结构体的开销,使用指向Coord3d的指针会更快:

package main

import (
    "fmt"
    "unsafe"
)

type Coord3d struct {
    X, Y, Z int64
}

func main() {
    var dummy *Coord3d
    fmt.Printf("sizeof(Coord3d) = %d\n", unsafe.Sizeof(*dummy))
}
英文:

In order to not to incur the overhead of initializing a structure, it would be faster to use a pointer to Coord3d:

package main

import (
    "fmt"
    "unsafe"
)

type Coord3d struct {
    X, Y, Z int64
}

func main() {
    var dummy *Coord3d
    fmt.Printf("sizeof(Coord3d) = %d\n", unsafe.Sizeof(*dummy))
}

huangapple
  • 本文由 发表于 2010年1月22日 07:18:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/2113751.html
匿名

发表评论

匿名网友

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

确定