返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

2.2 引用类型

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

特指 slicemapchannel 这三种预定义类型。

相比数字、数组等简单类型,引用类型有更复杂的内部结构。除分配内存外,还需初始化一系列属性,诸如指针、长度,甚至哈希分布、数据队列等。

从语言规范看,没有传统意义上的值类型和引用类型。只所以如此表述,除历史原因(早期文档)外,还因为它们与 make 函数的特殊关系,且被编译器和运行时特别对待(优化)。

      slice            array
  +-----------+       +-----//-----+
  |   array  -|-----> |  ...  ...  |
  +-----------+       +-----//-----+
  |   len     |
  +-----------+
  |   cap     |
  +-----------+
  
  |<-- new -->|                     
  |<------------- make ----------->|
  

以上图 slice 结构为例,由头和底层数组两部分组成。

  • new :按类型大小分配零值内存,返回指针。
  • make :转为类型构造函数(或指令),完成内部初始化。

实际上,底层数组不是切片的必然组成部分。详情参考后续章节。

函数 new 只关心类型长度( sizeof ),不涉及内部结构和逻辑。

func main() {
	s := *new([]int)             // slice: { ptr, len, cap }
	m := *new(map[string]int)    //   map: ptr
	c := *new(chan int)          //  chan: ptr

	println(unsafe.Sizeof(s))    // 24
	println(unsafe.Sizeof(m))    // 8
	println(unsafe.Sizeof(c))    // 8
}

/*

(gdb) x/3xg &s  ; 没有底层数组。
0xc000036740:	0x0000000000000000	0x0000000000000000
0xc000036750:	0x0000000000000000

(gdb) x/xg &m   ; 仅指针。
0xc000036708:	0x0000000000000000

(gdb) x/xg &c
0xc000036710:	0x0000000000000000

*/

/*

$ go build -gcflags "-N"
$ go tool objdump -S -s "main\.main" ./test

func main() {

    ; 不涉及内部分配和逻辑处理。

	...

	m := *new(map[string]int)

	MOVQ $0x0, 0x20(SP)	
	MOVQ 0x20(SP), AX	
	MOVQ AX, 0x8(SP)	

	c := *new(chan int)

	MOVQ $0x0, 0x18(SP)	
	MOVQ 0x18(SP), AX	
	MOVQ AX, 0x10(SP)
}
*/

虽然 new 的行为很像 malloc ,但优先在栈上分配。

func main() {
	p := new(int)
	*p = 100
}

/*

$ go build -gcflags "-N"
$ go tool objdump -S -s "main\.main" ./test

func main() {
	p := new(int)

	MOVQ $0x0, 0(SP)	
	LEAQ 0(SP), AX		
	MOVQ AX, 0x8(SP)	

	*p = 100
	MOVQ $0x64, 0(SP)	
}

*/

编译器将 make 翻译成 makeslicemakemap 之类的构造函数调用(或等价指令)。

相关实现细节,可参考下卷有关 map 的源码剖析章节。

func main() {
	s := make([]int, 10, 100)
	println(unsafe.Sizeof(s))  // 24
}

/*

(gdb) x/3xg &s  ; 分配底层数组,并初始化 len 和 cap 字段。
0xc000032758:	0x000000c000032438	0x000000000000000a
0xc000032768:	0x0000000000000064

*/
func main() {
	s := make([]int, 10000)
	s[0] = 1

	m := make(map[string]int, 10)
	m["a"] = 1
}

/*

$ go build -gcflags "-N"
$ go tool objdump -S -s "main\.main" ./test

func main() {

	s := make([]int, 10000)
		CALL runtime.makeslice(SB)	

	m := make(map[string]int, 10)
		CALL runtime.makemap(SB)

*/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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