在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
The unsafe Package in GolangGolang的unsafe包是一个很特殊的包。 为什么这样说呢? 本文将详细解释。 来自go语言官方文档的警告unsafe包的文档是这么说的:
Go 1 兼容性指南这么说:
当然包名称暗示unsafe包是不安全的。 但这个包有多危险呢? 让我们先看看unsafe包的作用。 Unsafe包的作用直到现在(Go1.7),unsafe包含以下资源:
这里,ArbitraryType不是一个真正的类型,它只是一个占位符。 与Golang中的大多数函数不同,上述三个函数的调用将始终在编译时求值,而不是运行时。 这意味着它们的返回结果可以分配给常量。 (BTW,unsafe包中的函数中非唯一调用将在编译时求值。当传递给len和cap的参数是一个数组值时,内置函数和cap函数的调用也可以在编译时被求值。) 除了这三个函数和一个类型外,指针在unsafe包也为编译器服务。 出于安全原因,Golang不允许以下之间的直接转换:
但是借助unsafe.Pointer,我们可以打破Go类型和内存安全性,并使上面的转换成为可能。这怎么可能发生?让我们阅读unsafe包文档中列出的规则:
这些规则与Go规范一致:
规则表明unsafe.Pointer类似于c语言中的void 。当然,void 在C语言里是危险的! 在上述规则下,对于两种不同类型T1和T2,可以使 T1值与unsafe.Pointer值一致,然后将unsafe.Pointer值转换为 T2值(或uintptr值)。通过这种方式可以绕过Go类型系统和内存安全性。当然,滥用这种方式是很危险的。 举个例子:
在这个例子中的转换可能是无意义的,但它是安全和合法的(为什么它是安全的?)。 因此,资源在unsafe包中的作用是为Go编译器服务,unsafe.Pointer类型的作用是绕过Go类型系统和内存安全。 再来一点 unsafe.Pointer 和 uintptr这里有一些关于unsafe.Pointer和uintptr的事实:
由于uintptr是一个整数类型,uintptr值可以进行算术运算。 所以通过使用uintptr和unsafe.Pointer,我们可以绕过限制,* T值不能在Golang中计算偏移量:
unsafe包有多危险关于unsafe包,Ian,Go团队的核心成员之一,已经确认:
所以,unsafe包中的三个函数看起来不危险。 go team leader甚至想把它们放在别的地方。 unsafe包中这几个函数唯一不安全的是它们调用结果可能在后来的版本中返回不同的值。 很难说这种不安全是一种危险。 看起来所有的unsafe包的危险都与使用unsafe.Pointer有关。 unsafe包docs列出了一些使用unsafe.Pointer合法或非法的情况。 这里只列出部分非法使用案例:
编译器很难检测Go程序中非法的unsafe.Pointer使用。 运行“go vet”可以帮助找到一些潜在的错误,但不是所有的都能找到。 同样是Go运行时,也不能检测所有的非法使用。 非法unsafe.Pointer使用可能会使程序崩溃或表现得怪异(有时是正常的,有时是异常的)。 这就是为什么使用不安全的包是危险的。 转换T1 为 T2对于将 T1转换为unsafe.Pointer,然后转换为 T2,unsafe包docs说:
这种“等效内存布局”的定义是有一些模糊的。 看起来go团队故意如此。 这使得使用unsafe包更危险。 由于Go团队不愿意在这里做出准确的定义,本文也不尝试这样做。 这里,列出了已确认的合法用例的一小部分, 合法用例1:在[]T和[]MyT之间转换在这个例子里,我们用int作为T:
在Golang中,[] int和[] MyInt是两种不同的类型,它们的底层类型是自身。 因此,[] int的值不能转换为[] MyInt,反之亦然。 但是在unsafe.Pointer的帮助下,转换是可能的:
合法用例2: 调用sync/atomic包中指针相关的函数sync / atomic包中的以下函数的大多数参数和结果类型都是unsafe.Pointer或*unsafe.Pointer:
要使用这些功能,必须导入unsafe包。 注意: unsafe.Pointer是一般类型,因此 unsafe.Pointer的值可以转换为unsafe.Pointer,反之亦然。
结论
原文:http://www.tapirgames.com/blog/golang-unsafe |
请发表评论