<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
日常開發中經常看到大佬們用各種 unsafe.Pointer, uintptr 搞各種花活,作為小白一看到 unsafe 就發憷,不瞭解二者的區別和場景,自然心裡沒數。今天我們就來學習下這部分知識。
uintptr 的定義在 builtin 包下,定義如下:
// uintptr is an integer type that is large enough to hold the bit pattern of // any pointer. type uintptr uintptr
參照註釋我們知道:
unsafe 包支援了這些方法來完成【型別】=> uintptr 的轉換:
func Sizeof(x ArbitraryType) uintptr func Offsetof(x ArbitraryType) uintptr func Alignof(x ArbitraryType) uintptr
你可以將任意型別變數轉入,獲取對應語意的 uintptr,用來後續計算記憶體地址(比如基於一個結構體欄位地址,獲取下一個欄位地址等)。
我們來看一下什麼是 unsafe 包下的 Pointer:
// ArbitraryType is here for the purposes of documentation only and is not actually // part of the unsafe package. It represents the type of an arbitrary Go expression. type ArbitraryType int // Pointer represents a pointer to an arbitrary type. There are four special operations // available for type Pointer that are not available for other types: // - A pointer value of any type can be converted to a Pointer. // - A Pointer can be converted to a pointer value of any type. // - A uintptr can be converted to a Pointer. // - A Pointer can be converted to a uintptr. // Pointer therefore allows a program to defeat the type system and read and write // arbitrary memory. It should be used with extreme care. type Pointer *ArbitraryType
這裡的 ArbitraryType 僅僅是為了便於開發者理解。語意上來講你可以把 Pointer 理解為一個可以指向任何一種型別的【指標】。
這一點很關鍵。我們此前遇到的場景一般都是,先定義一個型別,然後就有了這個型別對應的指標。而 unsafe.Pointer 則是一個通用的解法,不管你是什麼型別都可以。突破了這層限制,我們就可以在執行時具備更多能力,也方便適配一些通用場景。
官方提供了四種 Pointer 支援的場景:
這樣強大的能力使我們能夠繞開【型別系統】,丟失了編譯期的校驗,所以使用時一定要小心。
func Float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) }
我們取 f 的指標,將其轉為 unsafe.Pointer,再轉為一個 uint64 的指標,最後解出來值。
其實本質就是把 unsafe.Pointer 當成了一個媒介。用到了他可以從任意一個型別轉換得來,也可以轉為任意一個型別。
這樣的用法有一定的前提:
比如,int8 轉為 int64 是不支援的,我們測試一下:
package main import ( "fmt" "unsafe" ) func main() { fmt.Println("int8 => int64", Int8To64(5)) fmt.Println("int64 => int8", Int64To8(5)) } func Int64To8(f int64) int8 { return *(*int8)(unsafe.Pointer(&f)) } func Int8To64(f int8) int64 { return *(*int64)(unsafe.Pointer(&f)) }
執行後你會發現,int64 => int8 轉換正常,從小到大則會出問題:
int8 => int64 1079252997 int64 => int8 5 Program exited.
從 Pointer 轉 uintptr 本質產出的是這個 Pointer 指向的值的記憶體地址,一個整型。
這裡還是要在強調一下:
將一個指標轉為 uintptr 將會得到它指向的記憶體地址,而我們又可以結合 SizeOf,AlignOf,Offsetof 來計算出來另一個 uintptr 進行計算。
這類場景最常見的是【獲取結構體中的變數】或【陣列中的元素】。
比如:
f := unsafe.Pointer(&s.f) f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f)) e := unsafe.Pointer(&x[i]) e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))
上面這兩組運算本質是相同的,一種是直接拿地址,一種是通過計算 size,offset 來實現。
注意:變數到 uintptr 的轉換以及計算必須在一個表示式中完成(需要保證原子性):
錯誤的案例:
u := uintptr(p) p = unsafe.Pointer(u + offset)
uintptr 到 Pointer 的轉換一定要在一個表示式,不能用 uintptr 存起來,下個表示式再轉。
uintptr + offset 算地址,再跟 Pointer 轉化其實是一個很強大的能力,我們再來看一個實際的例子:
package main import ( "fmt" "unsafe" ) func main() { length := 6 arr := make([]int, length) for i := 0; i < length; i++ { arr[i] = i } fmt.Println(arr) // [0 1 2 3 4 5] // 取slice的第5個元素:通過計算第1個元素 + 4 個元素的size 得出 end := unsafe.Pointer(uintptr(unsafe.Pointer(&arr[0])) + 4*unsafe.Sizeof(arr[0])) fmt.Println(*(*int)(end)) // 4 fmt.Println(arr[4]) // 4 }
unsafe.Pointer 不能進行算數計算,uintptr 其實是很好的一個補充。
我們知道,reflect 的 Value 提供了兩個方法 Pointer 和 UnsafeAddr 返回 uintptr。這裡不使用 unsafe.Pointer 的用意在於避免使用者不 import unsafe 包就能將結果轉成任意型別,但這也帶來了問題。
上面有提到,千萬不能先儲存一個 uintptr,再轉 unsafe.Pointer,這樣的結果是很不可靠的。所以我們必須在呼叫完 Pointer/UnsafeAddr 之後就立刻轉 unsafe.Pointer。
正例:
p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))
反例:
u := reflect.ValueOf(new(int)).Pointer() p := (*int)(unsafe.Pointer(u))
活學活用,其實參照上面轉換的第一個案例就可以實現,不需要 uintptr。還是一樣的思路,用 unsafe.Pointer 作為媒介,指標轉換結束後,解指標拿到值即可。
import ( "unsafe" ) func BytesToString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } func StringToBytes(s string) []byte { return *(*[]byte)(unsafe.Pointer(&s)) }
其實這裡從 []byte 轉 string 的操作就是和 strings 包下 Builder 的設計一致的:
// A Builder is used to efficiently build a string using Write methods. // It minimizes memory copying. The zero value is ready to use. // Do not copy a non-zero Builder. type Builder struct { addr *Builder // of receiver, to detect copies by value buf []byte } // String returns the accumulated string. func (b *Builder) String() string { return *(*string)(unsafe.Pointer(&b.buf)) } // Reset resets the Builder to be empty. func (b *Builder) Reset() { b.addr = nil b.buf = nil } // Write appends the contents of p to b's buffer. // Write always returns len(p), nil. func (b *Builder) Write(p []byte) (int, error) { b.copyCheck() b.buf = append(b.buf, p...) return len(p), nil } // WriteString appends the contents of s to b's buffer. // It returns the length of s and a nil error. func (b *Builder) WriteString(s string) (int, error) { b.copyCheck() b.buf = append(b.buf, s...) return len(s), nil }
strings.Builder 設計之處就是為了最大程度降低記憶體拷貝。本質是維護了一個 buf 的位元組陣列。
sync.Pool 的設計中在本地 pool 沒有可以返回 Get 的元素時,會到其他 poolLocal 偷一個元素回來,這個跳轉到其他 pool 的操作就是用 unsafe.Pointer + uintptr + SizeOf 實現的,參考一下:
func indexLocal(l unsafe.Pointer, i int) *poolLocal { lp := unsafe.Pointer(uintptr(l) + uintptr(i)*unsafe.Sizeof(poolLocal{})) return (*poolLocal)(lp) }
到此這篇關於Golang 中的 unsafe.Pointer 和 uintptr詳解的文章就介紹到這了,更多相關Golang uintptr內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45