为什么在`unsafe.Sizeof()`中解引用空指针不会引发恐慌?

huangapple go评论83阅读模式
英文:

Why doesn't dereferencing a nil pointer in unsafe.Sizeof() cause a panic?

问题

这段代码为什么不会引发运行时恐慌?

英文:

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
}

Why doesn't this code

unsafe.Sizeof(*i)

cause a runtime panic?

答案1

得分: 6

规范:包unsafe:

AlignofOffsetofSizeof的调用是编译时常量表达式,类型为uintptr

这些函数在编译时进行求值,运行时不会发生实际的解引用操作。

这是因为只需要指向的值的信息,而不需要实际的解引用操作。

unsafe.Sizeof()中也有相关说明:

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

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

还可以参考规范:常量:

常量值由rune整数浮点数虚数字符串字面量、表示常量的标识符、常量表达式、具有常量结果的转换或应用于任何值的一些内置函数的结果值,例如unsafe.Sizeof,应用于一些表达式的caplen,应用于复数常量的realimag,以及应用于数字常量的complex

以下是类似的示例(如果不将它们传递给unsafe.Sizeof(),则会在运行时引发恐慌或无限期阻塞,但它们可以正常工作):

fmt.Println(unsafe.Sizeof(*(*int8)(nil))) // 1,没有解引用
fmt.Println(unsafe.Sizeof([]int16{}[10])) // 2,没有索引
x := "hi"
fmt.Println(unsafe.Sizeof(x[10]))                          // 1,没有索引
fmt.Println(unsafe.Sizeof(map[interface{}]int{}[[]int{}])) // 8,没有索引
fmt.Println(unsafe.Sizeof((interface{})(nil).(int16)))     // 2,没有类型断言
i := 0
fmt.Println(unsafe.Sizeof(i / i)) // 8,没有除以0
i = -1
fmt.Println(unsafe.Sizeof(1 << i))                // 8,没有负数的移位计数
fmt.Println(unsafe.Sizeof(make([]int, i)))        // 24,没有负长度
fmt.Println(unsafe.Sizeof((*[1]int)([]int{})))    // 8,没有转换为更大的数组
fmt.Println(unsafe.Sizeof((func() int32)(nil)())) // 4,没有函数调用
fmt.Println(unsafe.Sizeof(<-(chan int64)(nil)))   // 8,没有接收
var a, b interface{} = []int{}, []int{}
fmt.Println(unsafe.Sizeof(a == b)) // 1,没有比较

Go Playground上尝试这些示例。

英文:

Spec: Package unsafe:

> Calls to Alignof, Offsetof, and Sizeof are compile-time constant expressions of type uintptr.

These functions are evaluated at compile time, no actual dereferencing happens at runtime.

This is possible because the pointed value is not needed, only information about its type is needed, which does not need dereferencing.

It's also documented at unsafe.Sizeof():

> The return value of Sizeof is a Go constant.

Constants in Go are compile-time constants.

Also see Spec: Constants:

> A constant value is represented by a rune, integer, floating-point, imaginary, or string literal, an identifier denoting a constant, a constant expression, a conversion with a result that is a constant, or the result value of some built-in functions such as unsafe.Sizeof applied to any value, cap or len applied to some expressions, real and imag applied to a complex constant and complex applied to numeric constants.

See similar examples (which without passing them to unsafe.Sizeof() would panic at runtime or block indefinitely, but they work just fine):

fmt.Println(unsafe.Sizeof(*(*int8)(nil))) // 1, no dereferencing
fmt.Println(unsafe.Sizeof([]int16{}[10])) // 2, no indexing
x := &quot;hi&quot;
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 &lt;&lt; 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(&lt;-(chan int64)(nil)))   // 8, no receiving
var a, b interface{} = []int{}, []int{}
fmt.Println(unsafe.Sizeof(a == b)) // 1, no comparison

Try these on the Go Playground.

huangapple
  • 本文由 发表于 2022年2月10日 15:42:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/71061359.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定