2022-08-17:以下go语言代码输出什么?A:运行时 panic;B:32;C:编译错误;D:0。 package main func main() { var x *struct {

Posted 福大大架构师每日一题

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022-08-17:以下go语言代码输出什么?A:运行时 panic;B:32;C:编译错误;D:0。 package main func main() { var x *struct {相关的知识,希望对你有一定的参考价值。

2022-08-17:以下go语言代码输出什么?A:运行时 panic;B:32;C:编译错误;D:0。

package main

func main() 
  var x *struct 
    s [][32]byte
  

  println(len(x.s[99]))

答案选B。这道题很多人会选A,包括我也选A了。

1.选A的理由

定义一个结构体类型指针变量,即 x 是一个指针,指针类型是一个匿名结构体。很显然,x 的值是 nil,所以报panic也是很自然的。
println(x.s)
// panic: runtime error: invalid memory address or nil pointer dereference。

2.但这里为什么答案不是A呢?

原因在于len函数。
len 函数是一个内置类型,什么意思?就是由编译器实现的。它的参数可以接收多种类型,有泛型的味道。

func len(v Type) int
关于它的说明,标准库文档有说明:

内建函数 len 返回 v 的长度,这取决于具体类型:

数组:v 中元素的数量
数组指针:*v 中元素的数量(v 为 nil 时 panic)
切片、map:v 中元素的数量;若 v 为nil,len(v) 即为零
字符串:v 中字节的数量
通道:通道缓存中队列(未读取)元素的数量;若 v 为 nil,len(v) 即为零
光这个解释,还不够全面,len 函数还有其他一些特殊的点。这要看 Go 语言规范。

在规范中,有一节是关于 len 和 cap 的[1]。有如下几个要点:

返回结果总是 int;
返回结果有可能是常量;
有时对函数参数不求值,即编译期确定返回值;
2、3 点解释下。(规范中有说明)

如果 len 或 cap 的函数参数 v 是字符串常量,则返回值是一个常量。

如果 v 的类型是数组或指向数组的指针,且表达式 v 没有包含 channel 接收或(非常量)函数调用,则返回值也是一个常量。这种情况下,不会对 v 进行求值(即编译期就能确定)。否则返回值不是常量,且会对 v 进行求值(即得运行时确定)。

这一点是这道题的关键。

首先,x.s[99] 的类型是 [32]byte,它是一个数组,且表达式 x.s[99] 没有包含 channel 接收也不是函数调用,因此不会对 x.s[99] 进行求值,不求值自然不会 panic(想不明白?可以想成没有解引用操作)。也就是说,编译器能够在编译阶段分析出 x.s[99] 的类型是 [32]byte,且不需要对 x.s[99] 求值,因此直接返回数组的长度,即 32。

3.其他类似情况

类似这样不求值的情况还有没有?还真有。比如下面的代码:

var testdata *struct 
  a *[7]int

for i, _ := range testdata.a 
  fmt.Println(i)

同样不会 panic,原理和上面的类似,在 Go 语言规范有说明[2]。

“range” 子句中右侧的表达式被称为 range 表达式 ,它可以是数组、数组的指针、切片、字符串、map或是允许接收操作 的 channel。range 表达式会在开始此循环前被求值一次,但有一个例外:当存在最多一个迭代变量且 len(x) 是常量时,range 表达式是不被求值的。

所以上面代码中 testdata.a 不会被求值,因为 len(testdata.a) 是常量。

但如果改为这样:

var testdata *struct 
  a *[7]int

for i, j := range testdata.a 
  fmt.Println(i, j)

就会 panic。

以上是关于2022-08-17:以下go语言代码输出什么?A:运行时 panic;B:32;C:编译错误;D:0。 package main func main() { var x *struct {的主要内容,如果未能解决你的问题,请参考以下文章

2022-08-05:以下go语言代码输出什么?A:65, string;B:A, string;C:65, int;D:报错。

2022-07-20:以下go语言代码是关于json 和 context的,输出什么?A:{};B:{“a“:“b“};C:{“Context“:0};D:不确定。 package main imp

2022-07-28:以下go语言代码输出什么?A:AA;B:AB;C:BA;D:BB。 package main import ( “fmt“ ) func main() { f

2022-10-16:以下go语言代码输出什么?A:timed out;B:panic;C:没有任何输出。 package main import ( “context“ “fmt“

2022-07-08:以下go语言代码输出什么?A:3;B:+Inf;C:true;D:编译错误。 package main import “fmt“ func main() { var

2022-11-08:以下go语言代码输出什么?A:2;B:编译错误;C:运行 panic。 package main import “fmt“ func main() { a := []int