Golang中运行时参数是如何布局的?

Golang中运行时参数是如何布局的? 大家好,

我正在学习如何解读 panic 信息。据我所知,字符串作为指向数据和长度的指针传递,而结构体则会被展开。然而,当我用以下代码进行测试时,在 func hey 的末尾出现了一个 0x1。当我给它添加一个字段 z 并赋值时,这个值会随之改变。这代表什么意思,为什么会这样呢? playground

package main

import (
	"fmt"
	"unsafe"
)

type Nil int

func (n *Nil) hello() {
	fmt.Println(*n)
	panic("no no no")
}

type A struct {
	x string
	y *Nil
	//z int
}

func main() {
	var a A
	a.x = "string"
	none := Nil(2)
	a.y = &none
	//a.z = 3
	fmt.Println(unsafe.Sizeof(a))
	hey(a)
}

func hey(a A) {
	a.y.hello()

}
12
2
panic: no no no

goroutine 1 [running]:
main.(*Nil).hello(...)
	/tmp/sandbox662940585/prog.go:12
main.hey(0x115e1c, 0x6, 0x41a784, 0x1)
	/tmp/sandbox662940585/prog.go:32 +0xc0
main.main()
	/tmp/sandbox662940585/prog.go:28 +0xa0

Program exited: status 2.

更多关于Golang中运行时参数是如何布局的?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

最有可能的原因是为了确保栈对齐。

更多关于Golang中运行时参数是如何布局的?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,函数调用时参数的布局遵循特定的内存对齐规则。从你提供的panic信息来看,main.hey(0x115e1c, 0x6, 0x41a784, 0x1) 这四个值对应着传递给hey函数的参数。

让我们分析你的代码:

  1. A结构体包含一个string(16字节:8字节指针+8字节长度)和一个*Nil指针(8字节)
  2. 在64位系统上,结构体总大小为24字节(16+8)
  3. 但实际打印unsafe.Sizeof(a)显示12字节,这是因为你在32位环境中运行

在32位系统中:

  • string:8字节(4字节指针+4字节长度)
  • *Nil:4字节指针
  • 结构体对齐后:12字节

panic信息中的四个32位值(每个0x前缀的值):

  • 0x115e1c:字符串数据的指针
  • 0x6:字符串长度
  • 0x41a784*Nil指针
  • 0x1:填充/对齐字节

这个0x1是内存对齐产生的填充值。当添加z int字段时,结构体布局会改变,这个值也会相应变化。

示例代码演示参数布局:

package main

import (
	"fmt"
	"unsafe"
)

type A struct {
	x string
	y *int
	z int
}

func hey(a A) {
	panic("show layout")
}

func main() {
	var a A
	a.x = "hello"
	val := 42
	a.y = &val
	a.z = 100
	
	// 查看结构体布局
	fmt.Printf("Size: %d\n", unsafe.Sizeof(a))
	fmt.Printf("Offsets: x=%d, y=%d, z=%d\n",
		unsafe.Offsetof(a.x),
		unsafe.Offsetof(a.y),
		unsafe.Offsetof(a.z))
	
	hey(a)
}

输出会显示不同的参数值,其中可能包含对齐填充。在32位系统中,结构体参数可能被拆分为多个32位字进行传递,额外的值就是对齐填充的结果。

回到顶部