為什么 Go 關(guān)心 unsafe.Pointer 和 uintptr 之間的差別
點(diǎn)擊上方藍(lán)色“Go語(yǔ)言中文網(wǎng)”關(guān)注我們,領(lǐng)全套Go資料,每天學(xué)習(xí)?Go?語(yǔ)言
Go 有兩樣?xùn)|西或多或少是無(wú)類(lèi)型指針的表示:uintptr 和 unsafe.Pointer (和外表相反,它們是內(nèi)置類(lèi)型)。從表面上看這有點(diǎn)奇怪,因?yàn)?unsafe.Pointer 和 uintptr 可以彼此來(lái)回轉(zhuǎn)換。為什么不只有一種指針表現(xiàn)形式??jī)烧咧g有什么區(qū)別?
表面的區(qū)別是可以對(duì) uintptr 進(jìn)行算數(shù)運(yùn)算但不能對(duì) unsafe.Pointer(或任何其他 Go 指針)進(jìn)行運(yùn)算。unsafe 包的文檔指出了重要的區(qū)別:
uintptr 是整數(shù),不是引用。將 Pointer 轉(zhuǎn)換為 uintptr 會(huì)創(chuàng)建一個(gè)沒(méi)有指針語(yǔ)義的整數(shù)值。即使 uintptr 持有某個(gè)對(duì)象的地址,如果對(duì)象移動(dòng),垃圾收集器并不會(huì)更新 uintptr 的值,uintptr 也無(wú)法阻止該對(duì)象被回收。
盡管 unsafe.Pointer 是通用指針,但 Go 垃圾收集器知道它們指向 Go 對(duì)象;換句話說(shuō),它們是真正的 Go 指針。通過(guò)內(nèi)部魔法,垃圾收集器可以并且將使用它們來(lái)防止活動(dòng)對(duì)象被回收并發(fā)現(xiàn)更多活動(dòng)對(duì)象(如果unsafe.Pointer指向的對(duì)象自身持有指針)。因此,對(duì) unsafe.Pointer 的合法操作上的許多限制歸結(jié)為“在任何時(shí)候,它們都必須指向真正的 Go 對(duì)象”。如果創(chuàng)建的 unsafe.Pointer 并不符合,即使很短的時(shí)間,Go 垃圾收集器也可能會(huì)在該時(shí)刻掃描,然后由于發(fā)現(xiàn)了無(wú)效的 Go 指針而崩潰。
相比之下,uintptr 只是一個(gè)數(shù)字。這種特殊的垃圾收集魔法機(jī)制并不適用于 uintptr 所“引用”的對(duì)象,因?yàn)樗鼉H僅是一個(gè)數(shù)字,一個(gè) uintptr 不會(huì)引用任何東西。反過(guò)來(lái),這導(dǎo)致在將 unsafe.Pointer 轉(zhuǎn)換為 uintptr,對(duì)其進(jìn)行操作然后再將其轉(zhuǎn)回的各種方式上存在許多微妙的限制。基本要求是以這種方式進(jìn)行操作,使編譯器和運(yùn)行時(shí)可以屏蔽不安全的指針的臨時(shí)非指針性,使其免受垃圾收集器的干擾,因此這種臨時(shí)轉(zhuǎn)換對(duì)于垃圾收集將是原子的。
(我想,我在文章將內(nèi)存塊復(fù)制到 Go 結(jié)構(gòu)中[1]里對(duì) unsafe.Pointer 的使用是安全的,但我承認(rèn)我現(xiàn)在不確定。我相信 cgo 會(huì)有一些不可思議的機(jī)制,因?yàn)樗梢园踩刂圃斐霾话踩闹羔槪@些指針指向 C 內(nèi)存而不是 Go 內(nèi)存。)
PS:從 Go 1.8 開(kāi)始,即使當(dāng)時(shí)沒(méi)有運(yùn)行垃圾回收,所有 Go 指針必須始終有效(我相信也包括 unsafe.Pointer)。如果您在變量或字段中存儲(chǔ)了無(wú)效的指針,則僅通過(guò)將字段更新為包括 nil 在內(nèi)的完全有效的值即可使代碼崩潰。例如,請(qǐng)參閱這個(gè)有教育意義的 Go bug report[2]。
(我本想嘗試講一下內(nèi)部魔法,它允許垃圾收集器處理未類(lèi)型化的 unsafe.Pointer 指針,但我認(rèn)為對(duì)其了解不足,甚至無(wú)法說(shuō)出它使用的是哪種魔法。)
via: https://utcc.utoronto.ca/~cks/space/blog/programming/GoUintptrVsUnsafePointer
作者:ChrisSiebenmann[3]譯者:dust347[4]校對(duì):polaris1119[5]
本文由 GCTT[6] 原創(chuàng)編譯,Go 中文網(wǎng)[7] 榮譽(yù)推出
參考資料
[1]將內(nèi)存塊復(fù)制到 Go 結(jié)構(gòu)中: https://utcc.utoronto.ca/~cks/space/blog/programming/GoMemoryToStructures
[2]這個(gè)有教育意義的 Go bug report: https://github.com/golang/go/issues/19135
[3]ChrisSiebenmann: https://utcc.utoronto.ca/~cks/space/People/ChrisSiebenmann
[4]dust347: https://github.com/dust347
[5]polaris1119: https://github.com/polaris1119
[6]GCTT: https://github.com/studygolang/GCTT
[7]Go 中文網(wǎng): https://studygolang.com/
推薦閱讀
站長(zhǎng) polarisxu
自己的原創(chuàng)文章
不限于 Go 技術(shù)
職場(chǎng)和創(chuàng)業(yè)經(jīng)驗(yàn)
Go語(yǔ)言中文網(wǎng)
每天為你
分享 Go 知識(shí)
Go愛(ài)好者值得關(guān)注
