亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

golang如何使用指針靈活操作內(nèi)存及unsafe包原理解析

 更新時(shí)間:2024年07月06日 09:23:36   作者:golang架構(gòu)師k哥  
本文將深入探討unsafe包的功能和原理,同時(shí),我們學(xué)習(xí)某種東西,一方面是為了實(shí)踐運(yùn)用,另一方面則是出于功利性面試的目的,所以,本文還會(huì)為大家介紹unsafe?包的典型應(yīng)用以及高頻面試題,感興趣的朋友跟隨小編一起看看吧

Hi 你好,我是k哥。一個(gè)大廠(chǎng)工作6年,還在繼續(xù)搬磚的后端程序員。

我們都知道,C/C++提供了強(qiáng)大的萬(wàn)能指針void*,任何類(lèi)型的指針都可以和萬(wàn)能指針相互轉(zhuǎn)換。并且指針還可以進(jìn)行加減等算數(shù)操作。那么在Golang中,是否有類(lèi)似的功能呢?答案是有的,這就是我們今天要探討的unsafe包。

本文將深入探討unsafe包的功能和原理。同時(shí),我們學(xué)習(xí)某種東西,一方面是為了實(shí)踐運(yùn)用,另一方面則是出于功利性面試的目的。所以,本文還會(huì)為大家介紹unsafe 包的典型應(yīng)用以及高頻面試題。

功能

為了實(shí)現(xiàn)靈活操作內(nèi)存的目的,unsafe包主要提供了4個(gè)功能:

  • 定義了Pointer類(lèi)型,任何類(lèi)型的指針都可和Pointer互相轉(zhuǎn)換,類(lèi)似于c語(yǔ)言中的void*
var a int = 1
p := unsafe.Pointer(&a) // 其它類(lèi)型指針轉(zhuǎn)Pointer
b := (*int)(p) // Pointer類(lèi)型轉(zhuǎn)其它類(lèi)型指針
fmt.Println(*b) // 輸出1
  • 定義了uintptr類(lèi)型,Pointer和uintptr可以互相轉(zhuǎn)換, 從而實(shí)現(xiàn)指針的加減等算數(shù)運(yùn)算。
type Person struct {
    age int
    name string
}
person := Person{age:18,name:"k哥"}
p := unsafe.Pointer(&person) // 其它類(lèi)型指針轉(zhuǎn)Pointer
u := uintptr(p) // Pointer類(lèi)型轉(zhuǎn)為uintptr
u=u+8 // uintptr加減操作
pName := unsafe.Pointer(u) // uintptr轉(zhuǎn)換為Pointer
name := *(*string)(pName)
fmt.Println(name) // 輸出k哥

uintptr是用于指針運(yùn)算的,它只是一個(gè)存儲(chǔ)一個(gè) 指針地址 的 int 類(lèi)型,GC 不把 uintptr 當(dāng)指針,因此, uintptr 類(lèi)型的目標(biāo)可能會(huì)被回收

  • 獲取任意類(lèi)型內(nèi)存對(duì)齊、偏移量和內(nèi)存大小。
func Alignof(x ArbitraryType) uintptr // 內(nèi)存對(duì)齊
func Offsetof(x ArbitraryType) uintptr // 內(nèi)存偏移量
func Sizeof(x ArbitraryType) uintptr // 內(nèi)存大小
  • Alignof 返回類(lèi)型x的內(nèi)存地址對(duì)齊值m,這個(gè)類(lèi)型在內(nèi)存中的地址必須是m的倍數(shù)(基于內(nèi)存讀寫(xiě)性能的考慮)。
  • Offsetof 返回結(jié)構(gòu)體成員x在內(nèi)存中的位置離結(jié)構(gòu)體起始處(結(jié)構(gòu)體的第一個(gè)字段的偏移量都是0)的字節(jié)數(shù),即偏移量。
  • Sizeof 返回類(lèi)型 x 所占據(jù)的字節(jié)數(shù),如果類(lèi)型x結(jié)構(gòu)有指針,Sizeof不包含 x 指針成員所指向內(nèi)容的大小。

ArbitraryType是占位符,golang編譯器在編譯時(shí)會(huì)替換為具體類(lèi)型

  • 高性能類(lèi)型轉(zhuǎn)換。
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
func SliceData(slice []ArbitraryType) *ArbitraryType
func String(ptr *byte, len IntegerType) string 
func StringData(str string) *byte
  • Slice 傳入任意類(lèi)型的指針和長(zhǎng)度,返回該類(lèi)型slice變量
  • SliceData 傳入任意類(lèi)型的slice變量,返回該slice底層數(shù)組的指針。
  • String 從一個(gè)byte指針派生出一個(gè)指定長(zhǎng)度的字符串。
  • StringData 用來(lái)獲取一個(gè)字符串底層字節(jié)序列中的第一個(gè)byte的指針。

高性能類(lèi)型轉(zhuǎn)換原理

為什么說(shuō)Slice、SliceData、String、StringData是高性能類(lèi)型轉(zhuǎn)換函數(shù)呢?下面我們就來(lái)剖析下它們的實(shí)現(xiàn)原理。

本文以String和StringData函數(shù)為例,Slice和SliceData函數(shù)實(shí)現(xiàn)原理類(lèi)似。在介紹函數(shù)實(shí)現(xiàn)原理之前,先認(rèn)識(shí)下string類(lèi)型的底層數(shù)據(jù)結(jié)構(gòu)StringHeader。string類(lèi)型會(huì)被Golang編譯器編譯成此結(jié)構(gòu),其中Data是byte數(shù)組地址,Len是字符串長(zhǎng)度。

type StringHeader struct {
        Data uintptr // byte數(shù)組地址
        Len  int // 字符串長(zhǎng)度
}

String函數(shù)會(huì)被Go編譯成下面的函數(shù)實(shí)現(xiàn)邏輯。我們可以發(fā)現(xiàn),ptr指針轉(zhuǎn)換為string類(lèi)型,是直接將ptr賦值給StringHeader的成員Data,而不需要重新拷貝ptr指向的byte數(shù)組。從而通過(guò)零拷貝實(shí)現(xiàn)高性能類(lèi)型轉(zhuǎn)換。

import (
    "fmt"
    "reflect"
    "unsafe"
)
func String(ptr *byte, len int) string {
    p := (uintptr)(unsafe.Pointer(ptr))
    hdr := &reflect.StringHeader{
        Data: p,
        Len:  len,
    }
    // 將 StringHeader 轉(zhuǎn)為 string
    str := *(*string)(unsafe.Pointer(hdr))
    return str
}
func main() {
    bytes := []byte{'h', 'e', 'l', 'l', 'o'}
    ptr := &bytes[0]
    len := 5
    str := String(ptr, len)
    fmt.Println(str) // 輸出hello
}

StringData函數(shù)會(huì)被Go編譯成下面的函數(shù)實(shí)現(xiàn)邏輯。同理,我們可以發(fā)現(xiàn),string類(lèi)型轉(zhuǎn)換為byte,是直接取StringHeader的uintptr類(lèi)型成員Data,并將其轉(zhuǎn)換為byte。不需要拷貝整個(gè)string,重新生成byte數(shù)組。從而通過(guò)零拷貝實(shí)現(xiàn)高性能類(lèi)型轉(zhuǎn)換。

import (
    "fmt"
    "reflect"
    "unsafe"
)
func StringData(str string) *byte {
    hdr := (*reflect.StringHeader)(unsafe.Pointer(&str))
    data := hdr.Data
    return (*byte)(unsafe.Pointer(data))
}
func main() {
    str := "hello"
    data := StringData(str)
    fmt.Println(string(*data)) // 輸出h
}

回到問(wèn)題,為什么說(shuō)Slice、SliceData、String、StringData是高性能類(lèi)型轉(zhuǎn)換函數(shù)呢?通過(guò)String和StringData函數(shù)的實(shí)現(xiàn)邏輯,我們可以知道,String和StringData利用unsafe包,通過(guò)零拷貝,實(shí)現(xiàn)了高性能類(lèi)型轉(zhuǎn)換。

典型應(yīng)用

在實(shí)踐中,常見(jiàn)使用unsafe包的場(chǎng)景有2個(gè):

  • 與操作系統(tǒng)以及非go編寫(xiě)(cgo)的代碼通信。
func SetData(bytes []byte) { 
    cstr := (*C.char)(unsafe.Pointer(&bytes[0])) // 轉(zhuǎn)換成一個(gè)C char類(lèi)型
    C.setData(cstr, (C.int)(len(bytes))) // 調(diào)用C語(yǔ)言函數(shù)
}
  • 高性能類(lèi)型轉(zhuǎn)換。
func Bytes2String(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}
func String2Bytes(s string) []byte {
    sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
    bh := reflect.SliceHeader{
        Data: sh.Data,
        Len:  sh.Len,
        Cap:  sh.Len,
    }
    return *(*[]byte)(unsafe.Pointer(&bh))
}

高頻面試題

  • 能說(shuō)說(shuō)uintptr和unsafe.Pointer的區(qū)別嗎?
  • 字符串轉(zhuǎn)成byte數(shù)組,會(huì)發(fā)生內(nèi)存拷貝嗎?

到此這篇關(guān)于golang如何使用指針靈活操作內(nèi)存?unsafe包原理解析的文章就介紹到這了,更多相關(guān)golang unsafe包原內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語(yǔ)言基礎(chǔ)單元測(cè)試與性能測(cè)試示例詳解

    Go語(yǔ)言基礎(chǔ)單元測(cè)試與性能測(cè)試示例詳解

    這篇文章主要為大家介紹了Go語(yǔ)言基礎(chǔ)單元測(cè)試與性能測(cè)試示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進(jìn)步
    2021-11-11
  • 詳解Go語(yǔ)言中select語(yǔ)句的常見(jiàn)用法

    詳解Go語(yǔ)言中select語(yǔ)句的常見(jiàn)用法

    這篇文章主要是來(lái)和大家介紹一下Go語(yǔ)言中select?語(yǔ)句的常見(jiàn)用法,以及在使用過(guò)程中的注意事項(xiàng),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2023-07-07
  • Go語(yǔ)言context?test源碼分析詳情

    Go語(yǔ)言context?test源碼分析詳情

    這篇文章主要介紹了Go語(yǔ)言context?test源碼分析詳情,關(guān)于context?test,測(cè)試對(duì)象是context包,測(cè)試包的包名是context_test,下面將對(duì)context?test源碼進(jìn)行分析,需要的朋友可以參考一下,希望對(duì)你有所幫助
    2022-02-02
  • 深入探索Go語(yǔ)言中unsafe包的使用

    深入探索Go語(yǔ)言中unsafe包的使用

    Go語(yǔ)言的unsafe包被譽(yù)為黑科技,它為Go語(yǔ)言提供了底層訪(fǎng)問(wèn)和操控內(nèi)存的能力,本文將深入探討Go語(yǔ)言中unsafe包的使用方法和注意事項(xiàng),需要的可以參考一下
    2023-04-04
  • 在Golang中執(zhí)行Shell命令的教程詳解

    在Golang中執(zhí)行Shell命令的教程詳解

    在本教程中,我們將學(xué)習(xí)如何在 Golang 中執(zhí)行shell命令(如 ls、mkdir 或 grep ),我們還將學(xué)習(xí)如何通過(guò) stdin 和 stdout 傳遞 I/O 到正在運(yùn)行的命令,以及管理長(zhǎng)時(shí)間運(yùn)行的命令,感興趣的同學(xué)可以借鑒一下
    2023-05-05
  • Golang語(yǔ)言如何讀取http.Request中body的內(nèi)容

    Golang語(yǔ)言如何讀取http.Request中body的內(nèi)容

    這篇文章主要介紹了Golang語(yǔ)言如何讀取http.Request中body的內(nèi)容問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Go?為什么不支持可重入鎖原理解析

    Go?為什么不支持可重入鎖原理解析

    這篇文章主要為大家介紹了Go?為什么不支持可重入鎖原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Go語(yǔ)言實(shí)現(xiàn)將中文轉(zhuǎn)化為拼音功能

    Go語(yǔ)言實(shí)現(xiàn)將中文轉(zhuǎn)化為拼音功能

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中如何實(shí)現(xiàn)將中文轉(zhuǎn)化為拼音功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-01-01
  • 解決Golang在Web開(kāi)發(fā)時(shí)前端莫名出現(xiàn)的空白換行

    解決Golang在Web開(kāi)發(fā)時(shí)前端莫名出現(xiàn)的空白換行

    最近在使用Go語(yǔ)言開(kāi)發(fā)Web時(shí),在前端莫名出現(xiàn)了空白換行,找了網(wǎng)上的一些資料終于找到了解決方法,現(xiàn)在分享給大家,有需要的可以參考。
    2016-08-08
  • Go+Lua解決Redis秒殺中庫(kù)存與超賣(mài)問(wèn)題

    Go+Lua解決Redis秒殺中庫(kù)存與超賣(mài)問(wèn)題

    本文主要介紹了Go+Lua解決Redis秒殺中庫(kù)存與超賣(mài)問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03

最新評(píng)論