久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

golang中unsafe 和 uintptr 指針怎么用

235次閱讀
沒有評論

共計 2470 個字符,預計需要花費 7 分鐘才能閱讀完成。

這篇文章將為大家詳細講解有關 golang 中 unsafe 和 uintptr 指針怎么用,丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

1.golang 中的指針類型

三個類型
其實指針有三種:
一種是我們常見的 *,用 * 去表示的指針;
一種是 unsafe.Pointer,Pointer 是 unsafe 包下的一個類型;
最后一種是 uintptr,uintptr 這玩意是可以進行運算的也就是可以 ++–;

他們之間有這樣的轉換關系:
* = unsafe.Pointer = uintptr

有一點要注意的是,uintptr 并沒有指針的語義,意思就是 uintptr 所指向的對象會被 gc 無情地回收。而 unsafe.Pointer 有指針語義,可以保護它所指向的對象在“有用”的時候不會被垃圾回收。
從這樣的關系你大概就可以猜到,我們使用的指針 * p 轉換成 Pointer 然后轉換 uintptr 進行運算之后再原路返回,理論上就能等同于進行了指針的運算。我們下面就來實踐一下。

2. 具體操作

unsafe 操作 slice

func main() {s := make([]int, 10)s[1] = 2
 p :=  s[0]fmt.Println(*p)
 up := uintptr(unsafe.Pointer(p)) // 這里有可能會被回收   所以最好寫成  (*int)unsafe.Pointer(uintptr(unsafe.Pointer(p))+unsafe.Sizeof(int(0)))up += unsafe.Sizeof(int(0)) //  這里不是 up++p2 := (*int)(unsafe.Pointer(up))fmt.Println(*p2)}

輸出:
0
2

從代碼中我們可以看到,我們首先將指針指向切片的第一個位置,然后通過轉換得到 uintptr,操作 uintptr + 上 8 位(注意這里不能 ++ 因為存放的是 int,下一個元素位置相隔舉例 int 個字節),最后轉換回來得到指針,取值,就能取到切片的第二個位置了。

unsafe 操作 struct(可以訪問私有屬性)
我們知道如果一個結構體里面定義的屬性是私有的,那么這個屬性是不能被外界訪問到的。我們來看看下面這個操作:

package maintype User struct {age intname string}package mainfunc main() {user :=  User{}fmt.Println(user)
 s := (*int)(unsafe.Pointer(user))*s = 15up := uintptr(unsafe.Pointer(user)) + unsafe.Sizeof(int(0))namep := (*string)(unsafe.Pointer(up))*namep =  ljy  
 fmt.Println(user)}

User 是另外一個 basic 包中的結構體,其中的 age 是小寫開頭的,理論上來說,我們在外部沒有辦法修改 age 的值,但是經過上面這波操作之后,輸出信息是:
{0}
{10 xxx}
也就是說成功操作到了結構體的私有屬性。

順便提一句:創建結構體會被分配一塊連續的內存,結構體的地址也代表了第一個成員的地址。

字符串和 byte 數組轉換 inplace
我們知道如果將字符串轉換成 []byte 非常方便

s :=  123 a := []byte(s)

但是這樣需要開辟額外的空間,那么如何實現原地的,不需要拷貝數據的轉換呢?
其實從底層的存儲角度來說,string 的存儲規則和 []byte 是一樣的,也就是說,其實指針都是從某個位置開始到一段空間,中間一格一格。所以利用 unsafe 就可以做到。

func main() {s :=  123 a := []byte(s)print(s =   ,  s,  \n)print(a =   ,  a,  \n)
 a2 := (*[]byte)(unsafe.Pointer( s))print(a2 =   , a2,  \n)fmt.Println(*a2)} 輸出結果:s = 0xc420055f40a = 0xc420055f60a2 = 0xc420055f40[49 50 51]

我們可以看到 s 和 a 的地址是不一樣的,但是 s 和 a2 的地址是一樣的,并且 a2 已經是一個 []byte 了。

存在的問題
其實這個轉換是存在問題的,問題就在新的 []byte 的 Cap 沒有正確的初始化。
我們打印一下 cap 看一下

fmt.Println(“cap a =”, cap(a)) fmt.Println(“cap a2 =”, cap(*a2))  結果是:cap a = 32cap a2 = 17418400

問題的原因
在 src/reflect/value.go 下看

type StringHeader struct {
 Data uintptr
 Len int}type SliceHeader struct {
 Data uintptr
 Len int
 Cap int}

看到其實 string 沒有 cap 而 []byte 有,所以導致問題出現,也容易理解,string 是沒有容量擴容這個說法的,所以新的 []byte 沒有賦值 cap 所以使用了默認值。

問題解決

stringHeader := (*reflect.StringHeader)(unsafe.Pointer( s))bh := reflect.SliceHeader{
 Data: stringHeader.Data,
 Len: stringHeader.Len,
 Cap: stringHeader.Len,}return *(*[]byte)(unsafe.Pointer( bh))

通過重新設置 SliceHeader 就可以完成

關于“golang 中 unsafe 和 uintptr 指針怎么用”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-08-16發表,共計2470字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 普兰店市| 商水县| 纳雍县| 阿巴嘎旗| 保康县| 灌阳县| 通渭县| 洪湖市| 赫章县| 竹山县| 长阳| 获嘉县| 阿克陶县| 萨迦县| 杨浦区| 青河县| 社会| 上杭县| 历史| 长沙县| 丹巴县| 青河县| 体育| 张家港市| 惠东县| 宁南县| 墨脱县| 延庆县| 库伦旗| 平潭县| 吉林省| 阜平县| 普兰店市| 泽普县| 横山县| 西昌市| 上高县| 维西| 鄯善县| 朝阳区| 唐海县|