上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
5.5.2 内存
不管结构包含多少字段,其内存总是一次性分配,各字段(含匿名字段成员)在相邻地址空间按定义顺序(含对齐)排列。当然,对于引用类型、字符串和指针,结构内存中只包含其基本(头部)数据。
借助 unsafe
相关函数,输出所有字段的偏移量和长度。
package main import ( "fmt" . "unsafe" ) type Point struct { x, y int } type Value struct { id int name string data []byte next *Value Point } func main() { v := Value{ id: 1, name: "test", data: []byte{1, 2, 3, 4}, Point: Point{x: 100, y: 200}, } fmt.Printf("%p ~ %p, size: %d, align: %d\n", &v, Add(Pointer(&v), Sizeof(v)), Sizeof(v), Alignof(v)) s := "%p, %d, %d\n" fmt.Printf(s, &v.id, Offsetof(v.id), Sizeof(v.id)) fmt.Printf(s, &v.name, Offsetof(v.name), Sizeof(v.name)) fmt.Printf(s, &v.data, Offsetof(v.data), Sizeof(v.data)) fmt.Printf(s, &v.next, Offsetof(v.next), Sizeof(v.next)) fmt.Printf(s, &v.x, Offsetof(v.x), Sizeof(v.x)) fmt.Printf(s, &v.y, Offsetof(v.y), Sizeof(v.y)) } /* 0xc00005c0a0 ~ 0xc00005c0e8, size: 72, align: 8 field address offset size ------+--------------+--------+--------+---------------------- id 0xc00005c0a0 0 8 int name 0xc00005c0a8 8 16 string {ptr, len} data 0xc00005c0b8 24 24 slice {ptr, len, cap} next 0xc00005c0d0 48 8 pointer x 0xc00005c0d8 56 8 int y 0xc00005c0e0 64 8 int +0 +----------+ 0xc00005c0a0 | id | 8 +----------+ 0xc00005c0a8 | name.ptr | 16 +----------+ | name.len | 24 +----------+ 0xc00005c0b8 | data.ptr | 32 +----------+ | data.len | 40 +----------+ | data.cap | 48 +----------+ 0xc00005c0d0 | next | 56 +----------+ 0xc00005c0d8 | Point.x | 64 +----------+ 0xc00005c0e0 | Point.y | 72 +----------+ 0xc00005c0e8 */
对齐以所有字段中最长的基础类型宽度为准。编译器这么做的目的,即为了最大限度减少读写所需指令,也因为某些架构平台自身的要求。
package main import ( "fmt" . "unsafe" ) func main() { v1 := struct { a byte b byte c int32 // <-- int32 }{} v2 := struct { a byte b byte // <-- byte }{} v3 := struct { a byte b []int // <-- int c byte }{} fmt.Printf("v1: %d, %d\n", Alignof(v1), Sizeof(v1)) fmt.Printf("v2: %d, %d\n", Alignof(v2), Sizeof(v2)) fmt.Printf("v3: %d, %d\n", Alignof(v3), Sizeof(v3)) } /* v1: 4, 8 v2: 1, 2 v3: 8, 40 (gdb) x/2xw &v1 0xc000088e88: 0x00000201 0x00000003 +---+---+---+---+---+---+---+---+ | a | b | ? | ? | c | +---+---+---+---+---+---+---+---+ |<----- 4 ----->|<----- 4 ----->| (gdb) x/2xb &v2 0xc000088e86: 0x01 0x02 +---+---+ | a | b | +---+---+ 0 1 2 (gdb) x/5xg &v3 0xc000088f48: 0x0000000000000001 0x000000c000088e90 0xc000088f58: 0x0000000000000003 0x0000000000000003 0xc000088f68: 0x0000000000000002 +---+-------+-----------+-----------+-----------+---+-------+ | a | ... | b.ptr | b.len | b.cap | c | ... | +---+-------+-----------+-----------+-----------+---+-------+ |<--- 8 --->|<--- 8 --->|<--- 8 --->|<--- 8 --->|<--- 8 --->| */
func main() { d := struct { a [3]byte x int32 }{ a: [3]byte{1, 2, 3}, x: 100, } fmt.Println(d) } /* +---+---+---+---+---+---+---+---+ | 1 | 2 | 3 | ? | 100 | +---+---+---+---+---+---+---+---+ |<----- 4 ----->|<----- 4 ----->| */
空结构
如果空结构是最后一个字段,那么将其当做长度 1
的类型,避免越界。
其他零长度对象(
[0]int
)类似。
package main import ( "fmt" . "unsafe" ) func main() { v := struct { a struct{} b int c struct{} }{} fmt.Printf("%p ~ %p, size: %d, align: %d\n", &v, Add(Pointer(&v), Sizeof(v)), Sizeof(v), Alignof(v)) s := "%p, %d, %d\n" fmt.Printf(s, &v.a, Offsetof(v.a), Sizeof(v.a)) fmt.Printf(s, &v.b, Offsetof(v.b), Sizeof(v.b)) fmt.Printf(s, &v.c, Offsetof(v.c), Sizeof(v.c)) } /* 0xc000018070 ~ 0xc000018080, size: 16, align: 8 field address offset size ------+--------------+---------+--------- a 0xc000018070 0 0 b 0xc000018070 0 8 c 0xc000018078 8 0 +0 +----------+ 0xc000018070 | a, b | 8 +----------+ 0xc000018078 | c | 16 +----------+ 0xc000018080 */
如仅有一个空结构字段,那么按 1
对齐,只不过长度为 0
,且指向 zerobase
变量。
func main() { v := struct { a struct{} }{} fmt.Printf("%p, %d, %d\n", &v, Sizeof(v), Alignof(v)) } /* 0x5521d0, size: 0, align: 1 $ nm ./test | grep 5521d0 00000000005521d0 B runtime.zerobase */
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论