返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

5.5.2 内存

发布于 2024-10-12 19:15:48 字数 4804 浏览 0 评论 0 收藏 0

不管结构包含多少字段,其内存总是一次性分配,各字段(含匿名字段成员)在相邻地址空间按定义顺序(含对齐)排列。当然,对于引用类型、字符串和指针,结构内存中只包含其基本(头部)数据。

借助 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文