<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          淺析 unsafe.Pointer 與 uintptr

          共 2487字,需瀏覽 5分鐘

           ·

          2022-02-17 02:47

          看過 Go 相關(guān)源碼的同學(xué),應(yīng)該會(huì)注意到不少地方使用了 unsafe.Pointer 和 uintptr,單從類型名稱看,這些與“指針”是不是有什么關(guān)系?


          先附上一張關(guān)系圖,后面我們?cè)僬归_解析。

          普通指針類型

          我們一般將 *T 看作指針類型,表示一個(gè)指向 T 類型變量的指針。我們都知道 Go 是強(qiáng)類型語(yǔ)言,聲明變量之后,變量的類型是不可以改變的,不同類型的指針也不允許相互轉(zhuǎn)化。例如下面這樣:

          func?main(){
          ?i?:=?30
          ?iPtr1?:=?&i

          ?var?iPtr2?*int64?=?(*int64)(iPtr1)

          ?fmt.Println(iPtr2)
          }

          編譯報(bào)錯(cuò):cannot convert iPtr1 (type *int) to type *int64,提示不能進(jìn)行強(qiáng)制轉(zhuǎn)化。

          那怎么辦,如何實(shí)現(xiàn)相互轉(zhuǎn)化?

          還好 Go 官方提供了 unsafe 包,有相關(guān)的解決方案。

          unsafe.Pointer

          unsafe.Pointer 通用指針類型,一種特殊類型的指針,可以包含任意類型的地址,能實(shí)現(xiàn)不同的指針類型之間進(jìn)行轉(zhuǎn)換,類似于 C 語(yǔ)言里的 void* 指針。

          type?ArbitraryType?int

          type?Pointer?*ArbitraryType

          從定義可以看出,Pointer 實(shí)際上是 *int。

          官方文檔里還描述了 Pointer 的四種操作規(guī)則:

          1. 任何類型的指針都可以轉(zhuǎn)化成 unsafe.Pointer;
          2. unsafe.Pointer 可以轉(zhuǎn)化成任何類型的指針;
          3. uintptr 可以轉(zhuǎn)換為 unsafe.Pointer;
          4. unsafeP.ointer 可以轉(zhuǎn)換為 uintptr;

          不同類型的指針允許相互轉(zhuǎn)化實(shí)際上是運(yùn)用了第 1、2 條規(guī)則,我們就著例子看下:

          func?main(){
          ?i?:=?30
          ?iPtr1?:=?&i

          ?var?iPtr2?*int64?=?(*int64)(unsafe.Pointer(iPtr1))

          ?*iPtr2?=?8

          ?fmt.Println(i)
          }

          輸出:

          8

          上面的代碼,我們可以把 *int 轉(zhuǎn)為 *int64,并且對(duì)新的 *int64 進(jìn)行操作,從輸出會(huì)發(fā)現(xiàn) i 的值被改變了。

          可以說 unsafe.Pointer 是橋梁,可以讓任意類型的指針實(shí)現(xiàn)相互轉(zhuǎn)換。

          我們知道 Go 語(yǔ)言是不支持指針運(yùn)算,想要實(shí)現(xiàn)該怎么辦?

          看看第 3、4 條規(guī)則,uintptr 就可以派上用場(chǎng)了。

          uintptr

          源碼定義:

          //?uintptr?is?an?integer?type?that?is?large?enough?to?hold?the?bit?pattern?of
          //?any?pointer.
          type?uintptr?uintptr

          uintptr 是 Go 內(nèi)置類型,表示無符號(hào)整數(shù),可存儲(chǔ)一個(gè)完整的地址。常用于指針運(yùn)算,只需將 unsafe.Pointer 類型轉(zhuǎn)換成 uintptr 類型,做完加減法后,轉(zhuǎn)換成 unsafe.Pointer,通過 * 操作,取值或者修改值都可以。

          下面是一個(gè)通過指針偏移修改結(jié)構(gòu)體成員的例子,演示下 uintptr 的用法:

          type?Admin?struct?{
          ?Name?string
          ?Age?int
          }

          func?main(){
          ?admin?:=?Admin{
          ??Name:?"seekload",
          ??Age:?18,
          ?}
          ?ptr?:=?&admin
          ?name?:=?(*string)(unsafe.Pointer(ptr))???//?1

          ?*name?=?"四哥"

          ?fmt.Println(*ptr)

          ?age?:=?(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr))?+?unsafe.Offsetof(ptr.Age)))??//?2
          ?*age?=?35

          ?fmt.Println(*ptr)
          }

          輸出:

          {四哥?18}
          {四哥?35}

          特別提下,unsafe.Offsetof 的作用是返回成員變量 x 在結(jié)構(gòu)體當(dāng)中的偏移量,即返回結(jié)構(gòu)體初始內(nèi)存地址到 x 之間的字節(jié)數(shù)。

          //1 因?yàn)?strong style="color: black;">結(jié)構(gòu)體初始地址就是第一個(gè)成員的地址,又 Name 是結(jié)構(gòu)體第一個(gè)成員變量,所以此處不用偏移,我們拿到 admin 的地址,然后通過 unsafe.Pointer 轉(zhuǎn)為 *string,再進(jìn)行賦值操作即可。

          //2 成員變量 Age 不是第一個(gè)字段,想要修改它的值就需要內(nèi)存偏移。我們先將 admin 的指針轉(zhuǎn)化為 uintptr,再通過 unsafe.Offsetof() 獲取到 Age 的偏移量,兩者都是 uintptr,進(jìn)行相加指針運(yùn)算獲取到成員 Age 的地址,最后需要將 uintptr 轉(zhuǎn)化為 unsafe.Pointer,再轉(zhuǎn)化為 *int,才能對(duì) Age 操作。

          總結(jié)

          這篇文章我們簡(jiǎn)單介紹了普通指針類型、unsafe.Pointer 和 uintptr 之間的關(guān)系(見文章開頭關(guān)系圖),記住兩點(diǎn):

          1. unsafe.Pointer 可以實(shí)現(xiàn)不同類型指針之間相互轉(zhuǎn)化;
          2. uintptr 搭配著 unsafe.Pointer 使用,實(shí)現(xiàn)指針運(yùn)算;

          不過,官方不推薦使用 unsafe 包,正如它的命名一樣,是不安全的,比如涉及到內(nèi)存操作,這是繞過 Go 本身設(shè)計(jì)的安全機(jī)制的,不當(dāng)?shù)牟僮鳎赡軙?huì)破壞一塊內(nèi)存,而且這種問題非常不好定位。



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 39
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码屄网| 国产美女裸无遮挡裸体免费观软件 | 精品免费一区二区三区四区 | 黄网站免费XX | 日产精品一区二区乱码视频 |