最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang利用unsafe操作未導(dǎo)出變量-Pointer使用詳解

 更新時(shí)間:2018年08月06日 10:38:37   作者:豆瓣奶茶  
這篇文章主要給大家介紹了關(guān)于golang利用unsafe操作未導(dǎo)出變量-Pointer使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

unsafe.Pointer其實(shí)就是類似C的void *,在golang中是用于各種指針相互轉(zhuǎn)換的橋梁。uintptr是golang的內(nèi)置類型,是能存儲(chǔ)指針的整型,uintptr的底層類型是int,它和unsafe.Pointer可相互轉(zhuǎn)換。uintptr和unsafe.Pointer的區(qū)別就是:unsafe.Pointer只是單純的通用指針類型,用于轉(zhuǎn)換不同類型指針,它不可以參與指針運(yùn)算;而uintptr是用于指針運(yùn)算的,GC 不把 uintptr 當(dāng)指針,也就是說(shuō) uintptr 無(wú)法持有對(duì)象,uintptr類型的目標(biāo)會(huì)被回收。golang的unsafe包很強(qiáng)大,基本上很少會(huì)去用它。它可以像C一樣去操作內(nèi)存,但由于golang不支持直接進(jìn)行指針運(yùn)算,所以用起來(lái)稍顯麻煩。

切入正題。利用unsafe包,可操作私有變量(在golang中稱為“未導(dǎo)出變量”,變量名以小寫(xiě)字母開(kāi)始),下面是具體例子。

在$GOPATH/src下建立poit包,并在poit下建立子包p,目錄結(jié)構(gòu)如下:

$GOPATH/src

----poit

--------p

------------v.go

--------main.go

以下是v.go的代碼:

package p

import (
 "fmt"
)

type V struct {
 i int32
 j int64
}

func (this V) PutI() {
 fmt.Printf("i=%d\n", this.i)
}

func (this V) PutJ() {
 fmt.Printf("j=%d\n", this.j)
}

意圖很明顯,我是想通過(guò)unsafe包來(lái)實(shí)現(xiàn)對(duì)V的成員i和j賦值,然后通過(guò)PutI()和PutJ()來(lái)打印觀察輸出結(jié)果。

以下是main.go源代碼:

package main

import (
 "poit/p"
 "unsafe"
)

func main() {
 var v *p.V = new(p.V)
 var i *int32 = (*int32)(unsafe.Pointer(v))
 *i = int32(98)
 var j *int64 = (*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + uintptr(unsafe.Sizeof(int32(0)))))
 *j = int64(763)
 v.PutI()
 v.PutJ()
}

當(dāng)然會(huì)有些限制,比如需要知道結(jié)構(gòu)體V的成員布局,要修改的成員大小以及成員的偏移量。我們的核心思想就是:結(jié)構(gòu)體的成員在內(nèi)存中的分配是一段連續(xù)的內(nèi)存,結(jié)構(gòu)體中第一個(gè)成員的地址就是這個(gè)結(jié)構(gòu)體的地址,您也可以認(rèn)為是相對(duì)于這個(gè)結(jié)構(gòu)體偏移了0。相同的,這個(gè)結(jié)構(gòu)體中的任一成員都可以相對(duì)于這個(gè)結(jié)構(gòu)體的偏移來(lái)計(jì)算出它在內(nèi)存中的絕對(duì)地址。

具體來(lái)講解下main方法的實(shí)現(xiàn):

var v *p.V = new(p.V)

new是golang的內(nèi)置方法,用來(lái)分配一段內(nèi)存(會(huì)按類型的零值來(lái)清零),并返回一個(gè)指針。所以v就是類型為p.V的一個(gè)指針。

var i *int32 = (*int32)(unsafe.Pointer(v))

將指針v轉(zhuǎn)成通用指針,再轉(zhuǎn)成int32指針。這里就看到了unsafe.Pointer的作用了,您不能直接將v轉(zhuǎn)成int32類型的指針,那樣將會(huì)panic。剛才說(shuō)了v的地址其實(shí)就是它的第一個(gè)成員的地址,所以這個(gè)i就很顯然指向了v的成員i,通過(guò)給i賦值就相當(dāng)于給v.i賦值了,但是別忘了i只是個(gè)指針,要賦值得解引用。

*i = int32(98)

現(xiàn)在已經(jīng)成功的改變了v的私有成員i的值,好開(kāi)心_

但是對(duì)于v.j來(lái)說(shuō),怎么來(lái)得到它在內(nèi)存中的地址呢?其實(shí)我們可以獲取它相對(duì)于v的偏移量(unsafe.Sizeof可以為我們做這個(gè)事),但我上面的代碼并沒(méi)有這樣去實(shí)現(xiàn)。各位別急,一步步來(lái)。

var j *int64 = (*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + uintptr(unsafe.Sizeof(int32(0)))))

其實(shí)我們已經(jīng)知道v是有兩個(gè)成員的,包括i和j,并且在定義中,i位于j的前面,而i是int32類型,也就是說(shuō)i占4個(gè)字節(jié)。所以j是相對(duì)于v偏移了4個(gè)字節(jié)。您可以用uintptr(4)或uintptr(unsafe.Sizeof(int32(0)))來(lái)做這個(gè)事。unsafe.Sizeof方法用來(lái)得到一個(gè)值應(yīng)該占用多少個(gè)字節(jié)空間。注意這里跟C的用法不一樣,C是直接傳入類型,而golang是傳入值。之所以轉(zhuǎn)成uintptr類型是因?yàn)樾枰鲋羔樳\(yùn)算。v的地址加上j相對(duì)于v的偏移地址,也就得到了v.j在內(nèi)存中的絕對(duì)地址,別忘了j的類型是int64,所以現(xiàn)在的j就是一個(gè)指向v.j的指針,接下來(lái)給它賦值:

*j = int64(763)

好吧,現(xiàn)在貌視一切就緒了,來(lái)打印下:

v.PutI()
v.PutJ()

如果您看到了正確的輸出,那恭喜您,您做到了!

但是,別忘了上面的代碼其實(shí)是有一些問(wèn)題的,您發(fā)現(xiàn)了嗎?

在p目錄下新建w.go文件,代碼如下:

package p

import (
 "fmt"
 "unsafe"
)

type W struct {
 b byte
 i int32
 j int64
}

func init() {
 var w *W = new(W)
 fmt.Printf("size=%d\n", unsafe.Sizeof(*w))
}

需要修改main.go的代碼嗎?不需要,我們只是來(lái)測(cè)試一下。w.go里定義了一個(gè)特殊方法init,它會(huì)在導(dǎo)入p包時(shí)自動(dòng)執(zhí)行,別忘了我們有在main.go里導(dǎo)入p包。每個(gè)包都可定義多個(gè)init方法,它們會(huì)在包被導(dǎo)入時(shí)自動(dòng)執(zhí)行(在執(zhí)行main方法前被執(zhí)行,通常用于初始化工作),但是,最好在一個(gè)包中只定義一個(gè)init方法,否則您或許會(huì)很難預(yù)期它的行為)。我們來(lái)看下它的輸出:

size=16

等等,好像跟我們想像的不一致。來(lái)手動(dòng)計(jì)算一下:b是byte類型,占1個(gè)字節(jié);i是int32類型,占4個(gè)字節(jié);j是int64類型,占8個(gè)字節(jié),1+4+8=13。這是怎么回事呢?這是因?yàn)榘l(fā)生了對(duì)齊。在struct中,它的對(duì)齊值是它的成員中的最大對(duì)齊值。每個(gè)成員類型都有它的對(duì)齊值,可以用unsafe.Alignof方法來(lái)計(jì)算,比如unsafe.Alignof(w.b)就可以得到b在w中的對(duì)齊值。同理,我們可以計(jì)算出w.b的對(duì)齊值是1,w.i的對(duì)齊值是4,w.j的對(duì)齊值也是4。如果您認(rèn)為w.j的對(duì)齊值是8那就錯(cuò)了,所以我們前面的代碼能正確執(zhí)行(試想一下,如果w.j的對(duì)齊值是8,那前面的賦值代碼就有問(wèn)題了。也就是說(shuō)前面的賦值中,如果v.j的對(duì)齊值是8,那么v.i跟v.j之間應(yīng)該有4個(gè)字節(jié)的填充。所以得到正確的對(duì)齊值是很重要的)。對(duì)齊值最小是1,這是因?yàn)榇鎯?chǔ)單元是以字節(jié)為單位。所以b就在w的首地址,而i的對(duì)齊值是4,它的存儲(chǔ)地址必須是4的倍數(shù),因此,在b和i的中間有3個(gè)填充,同理j也需要對(duì)齊,但因?yàn)閕和j之間不需要填充,所以w的Sizeof值應(yīng)該是13+3=16。如果要通過(guò)unsafe來(lái)對(duì)w的三個(gè)私有成員賦值,b的賦值同前,而i的賦值則需要跳過(guò)3個(gè)字節(jié),也就是計(jì)算偏移量的時(shí)候多跳過(guò)3個(gè)字節(jié),同理j的偏移可以通過(guò)簡(jiǎn)單的數(shù)學(xué)運(yùn)算就能得到。

比如也可以通過(guò)unsafe來(lái)靈活取值:

package main

import (
 "fmt"
 "unsafe"
)

func main() {
 var b []byte = []byte{'a', 'b', 'c'}
 var c *byte = &b[0]
 fmt.Println(*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(c)) + uintptr(1))))
}

關(guān)于填充,F(xiàn)astCGI協(xié)議就用到了。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Golang單元測(cè)試與斷言編寫(xiě)流程詳解

    Golang單元測(cè)試與斷言編寫(xiě)流程詳解

    這篇文章主要介紹了Golang單元測(cè)試與斷言編寫(xiě)流程,單元測(cè)試也是一個(gè)很重要的事情。單元測(cè)試是指在開(kāi)發(fā)中,對(duì)一個(gè)函數(shù)或模塊的測(cè)試。其強(qiáng)調(diào)的是對(duì)單元進(jìn)行測(cè)試
    2022-12-12
  • GO語(yǔ)言實(shí)現(xiàn)的端口掃描器分享

    GO語(yǔ)言實(shí)現(xiàn)的端口掃描器分享

    這篇文章主要介紹了GO語(yǔ)言實(shí)現(xiàn)的端口掃描器分享,本文直接給出實(shí)現(xiàn)代碼,代碼中包含大量注釋,需要的朋友可以參考下
    2014-10-10
  • Go 語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之雙鏈表學(xué)習(xí)教程

    Go 語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之雙鏈表學(xué)習(xí)教程

    這篇文章主要為大家介紹了Go 語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之雙鏈表學(xué)習(xí)教程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Go函數(shù)全景從基礎(chǔ)到高階深度剖析

    Go函數(shù)全景從基礎(chǔ)到高階深度剖析

    本文深入探索Go語(yǔ)言中的函數(shù)特性,從基礎(chǔ)函數(shù)定義到特殊函數(shù)類型,再到高階函數(shù)的使用和函數(shù)調(diào)用優(yōu)化,通過(guò)詳細(xì)的代碼示例和專業(yè)解析,讀者不僅可以掌握函數(shù)的核心概念,還能了解如何在實(shí)踐中有效利用這些特性來(lái)提高代碼質(zhì)量和性能
    2023-10-10
  • 一文帶你深入理解Go語(yǔ)言中的sync.Cond

    一文帶你深入理解Go語(yǔ)言中的sync.Cond

    sync.Cond?表示的是條件變量,它是一種同步機(jī)制,用來(lái)協(xié)調(diào)多個(gè)?goroutine?之間的同步。本文將通過(guò)示例為大家介紹Go語(yǔ)言中sync.Cond的使用,需要的可以參考一下
    2023-01-01
  • go?mod詳細(xì)使用教程

    go?mod詳細(xì)使用教程

    go mod是go的一個(gè)模塊管理工具,用來(lái)代替?zhèn)鹘y(tǒng)的GOPATH方案,下面這篇文章主要給大家介紹了關(guān)于go?mod詳細(xì)使用的相關(guān)資料,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • Go異步任務(wù)解決方案之Asynq庫(kù)詳解

    Go異步任務(wù)解決方案之Asynq庫(kù)詳解

    需要在Go應(yīng)用程序中異步處理任務(wù)? Asynq,簡(jiǎn)單高效的任務(wù)隊(duì)列實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于Go異步任務(wù)解決方案之Asynq庫(kù)的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • GoLang jwt無(wú)感刷新與SSO單點(diǎn)登錄限制解除方法詳解

    GoLang jwt無(wú)感刷新與SSO單點(diǎn)登錄限制解除方法詳解

    這篇文章主要介紹了GoLang jwt無(wú)感刷新與SSO單點(diǎn)登錄限制解除方法,JWT是一個(gè)簽名的JSON對(duì)象,通常用作Oauth2的Bearer token,JWT包括三個(gè)用.分割的部分。本文將利用JWT進(jìn)行認(rèn)證和加密,感興趣的可以了解一下
    2023-03-03
  • Golang并發(fā)編程之GMP模型詳解

    Golang并發(fā)編程之GMP模型詳解

    傳統(tǒng)的并發(fā)編程模型是基于線程和共享內(nèi)存的同步訪問(wèn)控制的,共享數(shù)據(jù)受鎖的保護(hù),線程將爭(zhēng)奪這些鎖以訪問(wèn)數(shù)據(jù)。本文將介紹Go并發(fā)編程中的GMP模型,感興趣的可以了解一下
    2023-03-03
  • golang json性能分析詳解

    golang json性能分析詳解

    json格式可以算我們?nèi)粘W畛S玫男蛄谢袷街涣?,Go語(yǔ)言作為一個(gè)由Google開(kāi)發(fā),號(hào)稱互聯(lián)網(wǎng)的C語(yǔ)言的語(yǔ)言,自然也對(duì)JSON格式支持很好。下面這篇文章主要給大家詳細(xì)分析介紹了golang json性能的相關(guān)資料,需要的朋友可以參考下。
    2018-02-02

最新評(píng)論

偏关县| 繁昌县| 都兰县| 浦县| 岢岚县| 盱眙县| 利川市| 团风县| 临洮县| 岗巴县| 东乌珠穆沁旗| 柏乡县| 台安县| 呼图壁县| 西峡县| 平乐县| 甘南县| 彭阳县| 资阳市| 鹤峰县| 卓资县| 隆德县| 茶陵县| 宁海县| 安西县| 樟树市| 景宁| 象州县| 大邑县| 逊克县| 万源市| 河东区| 建阳市| 江津市| 凉山| 樟树市| 榆林市| 丰镇市| 潍坊市| 会泽县| 长春市|