当前位置: 首页 > 科技观察

对Go的第一印象-1.17正式版

时间:2023-03-20 14:11:10 科技观察

8月17日凌晨,Go1.17正式发布!迫不及待地阅读发行说明:https://golang.google.cn/doc/go1.17。语言更改此版本主要包含三个小语法(糖)增强功能:添加了将切片对象直接转换为数组指针的功能。在unsafe中增加了Add功能。在unsafe中添加了Slice功能。slicetoarraypointer这是Go语言规范中新增的内容:https://golang.google.cn/ref/spec#Conversions_from_slice_to_array_pointer。直接上用例:从上图的代码可以看出,有了这个新的语法功能,类型转换确实方便很多。但是,如果转换后的目标数组的长度(len)大于切片的长度(len),虽然编译成功,但在运行时肯定会panic。这是因为:Go编译器知道切片的长度是4,目标数组的长度是5,这是数组越界访问,是错误的,所以源码如下:a5:=(*[5]int)(slice)fmt.Println("a5=",*a5)直接换成了下面的runtime.panicSliceConvert函数调用,导致进程异常退出。这是Go语言中一个很奇怪的现象:即使在编译过程中发现代码异常,编译成功,异常被编码成runtimepanic。这种情况遇到过好几次了。如果在Go1.17之前实现了slice转数组指针的功能,实现如下,稍微复杂一点:Go1.17完全兼容老版本的语法,代码在Go中运行没有问题1.17.只是数组越界的问题需要开发者慎重处理。unsafe.Add这是unsafe/unsafe.go源码文件中新加入的内置函数。该函数没有函数体,由Go编译器实现。它的实现相当于如下代码:funcAdd(ptrPointer,lenIntegerType)Pointer{returnPointer(uintptr(ptr)+uintptr(len))}相关源码链接:https://github.com/golang/go/blob/go1.17/src/unsafe/unsafe.go#L217https://github.com/golang/go/blob/go1.17/src/go/types/builtins.go#L589unsafe.Slice这是不安全/不安全的。gosourcefile中新加入的内置函数,没有函数体,由Go编译器实现。此函数的行为类似于通用函数。funcSlice(ptr*ArbitraryType,lenIntegerType)[]ArbitraryType{return(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]}相关源码链接:https://github.com/golang/go/blob/go1.17/src/unsafe/unsafe.go#L233https://github.com/golang/go/blob/go1.17/src/go/types/builtins.go#L690如果没有特别标记调用堆栈边界检查,Go编译器会自动在函数入口添加一条指令来检查是否需要扩栈。在Go1.17之前的版本中,检查是通过从FS寄存器读取线程本地存储(TLS)中的堆栈保护标志(runtime.g.stackguard0)并将其与RSP寄存器进行比较来完成的。在Go1.17版本中,发现这个检查发现了一个变化:检查是通过比较R14寄存器和RSP寄存器来实现的。这个检查从4条指令减少到2条指令,效率肯定提高了很多,因为这个检查几乎涵盖了开发者实现的所有Go函数。这是一个重大更新。由于时间关系,没有进一步研究其细节。调用约定在简单的调试过程中,我发现Go1.17版本的函数调用使用RAX寄存器作为返回值,参数和寄存器使用。在Go1.17之前的版本中,开发者实现的所有Go函数都使用栈内存来传递参数和返回值;只有少数汇编实现的函数、一些特殊函数和系统调用使用寄存器来传递参数和返回值。在此版本中,寄存器用于参数和返回值。它似乎正在向UNIX环境中的函数调用约定靠拢。这是一个重大更新。毕竟寄存器的数量是有限的,哪些寄存器是用来传递参数和返回值的,哪些参数需要通过栈内存来传递,需要找空去摸索。此更改记录在发行说明的编译器部分:https://golang.google.cn/doc/go1.17#compiler。对于其他可移植性,添加了对新系统和处理器架构的支持。在工具链方面,也发生了一些变化。本文转载自微信公众号「记忆中的Golang」