为什么在 unsafe.Sizeof() 中取消引用 nil 指针不会导致恐慌?

xia*_*gle 5 pointers runtime-error go

https://go.dev/play/p/X_BH4qGgXHJ

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    var i *int
    fmt.Println(unsafe.Sizeof(*i)) // dereference of null pointer i
}

Run Code Online (Sandbox Code Playgroud)

为什么这段代码没有

unsafe.Sizeof(*i)
Run Code Online (Sandbox Code Playgroud)

导致运行时恐慌?

icz*_*cza 6

规格: 包装不安全:

AlignofOffsetof、 和 的调用Sizeof是类型的编译时常量表达式uintptr

这些函数在编译时评估,运行时不会发生实际的取消引用。

这是可能的,因为不需要指向的值,只需要有关其类型的信息,这不需要取消引用。

它还记录在unsafe.Sizeof()

Sizeof 的返回值是一个 Go 常量。

Go 中的常量是编译时常量。

另请参阅规范:常量:

常量值由符文整数浮点虚数字符串文字、表示常量的标识符、常量表达式、结果为常量的转换或某些内置函数的结果值表示。函数例如unsafe.Sizeof应用于任何值caplen应用于某些表达式,应用于复数常量和应用于数字常量realimag

查看类似的示例(如果不传递它们,则会unsafe.Sizeof()在运行时出现恐慌或无限期阻塞,但它们工作得很好):

fmt.Println(unsafe.Sizeof(*(*int8)(nil))) // 1, no dereferencing
fmt.Println(unsafe.Sizeof([]int16{}[10])) // 2, no indexing
x := "hi"
fmt.Println(unsafe.Sizeof(x[10]))                          // 1, no indexing
fmt.Println(unsafe.Sizeof(map[interface{}]int{}[[]int{}])) // 8, no indexing
fmt.Println(unsafe.Sizeof((interface{})(nil).(int16)))     // 2, no type assertion
i := 0
fmt.Println(unsafe.Sizeof(i / i)) // 8, no division by 0
i = -1
fmt.Println(unsafe.Sizeof(1 << i))                // 8, no negative shift count
fmt.Println(unsafe.Sizeof(make([]int, i)))        // 24, no negative length
fmt.Println(unsafe.Sizeof((*[1]int)([]int{})))    // 8, no converting to bigger array
fmt.Println(unsafe.Sizeof((func() int32)(nil)())) // 4, no function call
fmt.Println(unsafe.Sizeof(<-(chan int64)(nil)))   // 8, no receiving
var a, b interface{} = []int{}, []int{}
fmt.Println(unsafe.Sizeof(a == b)) // 1, no comparison
Run Code Online (Sandbox Code Playgroud)

在Go Playground上尝试这些。