package main
import (
"fmt"
"unsafe"
)
type A struct {
a bool
b int64
c int
}
type B struct {
b int64
a bool
c int
}
type C struct {
}
func main() {
// output 24
fmt.Println(unsafe.Sizeof(A{}))
// output 16
fmt.Println(unsafe.Sizeof(B{}))
// output 0
fmt.Println(unsafe.Sizeof(C{}))
}
结构A
和B
具有相同的字段,但是如果以不同的顺序指定,它们将导致大小不同。为什么?
结构的大小C
为零。系统为多少内存分配a := C{}
?
谢谢。
TL; DR; (摘要):如果对字段进行重新排序,将使用不同的隐式填充,并且隐式填充计入的大小struct
。
注意结果取决于目标体系结构。
适用于您发布结果时GOARCH=386
,但是当GOARCH=amd64
,既大小的A{}
和B{}
将是24个字节。
结构的字段地址必须对齐,并且类型的字段地址int64
必须为8字节的倍数。规格:包装unsafe
:
计算机体系结构可能需要 对齐 内存地址; 也就是说,如果变量的地址是一个因子的倍数,则该变量的类型为 alignment
。该函数Alignof
采用表示任何类型变量的表达式,并以字节为单位返回(变量的类型)对齐方式。
对齐int64
为8个字节:
fmt.Println(unsafe.Alignof((int64(0)))) // Prints 8
因此,在A
第一个字段为的情况下,bool
后面有7个字节的隐式填充,A.a
因此该A.b
类型的填充int64
可以在8的倍数的地址上开始。这保证了(确切地需要7个字节的填充)为在struct
本身对准这是8的倍数的地址,因为这是所有字段的最大尺寸。请参阅:规格:尺寸对齐保证:
对于
x
结构类型的变量:unsafe.Alignof(x)
是的unsafe.Alignof(x.f)
每个字段f
的所有值中的最大值x
,但至少是1
。
在B
(如果GOARCH=386
是您的情况)情况下,在B.a
type字段之后将只有3个字节的隐式填充,bool
因为此字段后面是type字段int
(其大小为4个字节),而不是int64
。
int
如果对齐则为4个字节,如果对齐为GOARCH=386
8个字节GOARCH=amd64
:
fmt.Println(unsafe.Alignof((int(0)))) // Prints 4 if GOARCH=386, and 8 if GOARCH=amd64
使用unsafe.Offsetof()
找出领域的偏移量:
// output 24
a := A{}
fmt.Println(unsafe.Sizeof(a),
unsafe.Offsetof(a.a), unsafe.Offsetof(a.b), unsafe.Offsetof(a.c))
// output 16
b := B{}
fmt.Println(unsafe.Sizeof(b),
unsafe.Offsetof(b.b), unsafe.Offsetof(b.a), unsafe.Offsetof(b.c))
// output 0
fmt.Println(unsafe.Sizeof(C{}))
var i int
fmt.Println(unsafe.Sizeof(i))
输出if GOARCH=386
(在Go Playground上尝试):
24 0 8 16
16 0 8 12
0
4
如果输出GOARCH=amd64
:
24 0 8 16
24 0 8 16
0
8
规格:尺寸对齐保证:
如果结构或数组类型不包含大小大于零的字段(或元素),则其大小为零。 两个不同的零大小变量在内存中可能具有相同的地址。
因此,该规范仅提示使用相同的内存地址,但这不是必需的。但是,当前的实现遵循它。也就是说,不会为大小为零的类型的值分配任何内存,这包括空结构struct{}
和长度为零的数组,例如[0]int
,或元素大小为零(且具有任意长度)的数组。
请参阅以下示例:
a := struct{}{}
b := struct{}{}
c := [0]int{}
d := [3]struct{}{}
fmt.Printf("%p %p %p %p %p", &a, &b, &c, &d, &d[2])
输出(在Go Playground上尝试):所有地址都相同。
0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c 0x21cd7c
有关有趣且相关的主题,请阅读: Dave
Cheney:填充很难
为什么在添加相同的数字时输出不同? 输出为: 如果我交换值 我得到的输出为:<代码>15.7000000000001 如何获得相同的输出?
我正在尝试将以下YAML数据分解为Go结构。 数据采用以下格式: 根据类型字段,我想确定是否将配置字段解组到awsConfig或kubernetesConfig结构中。 我当前的代码如下(使用“gopkg.in/yaml.v2”): 操场代码:https://go.dev/play/p/klxOoHMCtnG 目前它被解组为 map[接口 {}]接口 {},无法转换为上述结构之一。错误:panic
问题内容: 如何获得按字段排序的struct输出? 问题答案: A 是字段的 有序 集合。该包使用反射来获取值的字段和值,并按照定义它们的顺序生成输出。 因此,最简单的解决方案是在已经按字母顺序排列字段的位置声明类型: 如果您不能修改字段的顺序(例如,内存布局很重要),则可以通过为结构类型指定一个方法来实现接口: 所述包检查所传递的值工具,并且如果是的话,调用它的方法,以产生输出。 该解决方案的缺
查询:{“explain”:true,“size”:500,“Query”:{“query_string”:{“query_string”:{“query_string”:“((names.name:(BANK AMERICA\\)”)^50或(names.name:(BANK AMERICA\))^30或(name_pair:\“BANK AMERICA\\\)^30或(name_name:(B
问题内容: 这里有点复杂的SQL问题。 我目前有一个与多个字段匹配的SELECT语句,就像这样。 我想修改该语句,以使其使用OR而不是AND,从而选择与任何字段匹配的所有记录。 下一步是使用评分系统对结果进行排名。 所以… 匹配1-如果field2和field 3匹配,则得分为1400 匹配2-如果field1和field 4匹配,则得分将为1010 比赛1将在结果顶部。 对于使用SQL实现此目标
我在oracle数据库上有一个奇怪的行为。我们制作了大约310万条记录的巨大插入。到目前为止一切都很好。 插入完成后不久(大约 1 太 10 分钟),我执行两个语句。 从表中选择计数(*)。 从表中选择* 第一条语句的结果很好,它给出了插入的行的确切数量。 第二个语句的结果现在是问题所在。例如,根据时间的不同,返回的行数比第一个语句的结果低约 500K。两种结果的差异随着时间的推移而减少。 因此,