<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                Go 語言指針 我們都知道,變量是一種使用方便的占位符,用于引用計算機內存地址。 Golang 支持指針類型 *T,指針的指針 **T,以及包含包名前綴的 *<package>.T。 ~~~ ? 默認值 nil,沒有 NULL 常量。 ? 操作符 "&" (取地址符) 取變量地址,"*" (取值符)透過指針訪問目標對象。 ? 不支持指針運算,不支持 "->" 運算符,直接用 "." 訪問目標成員。 ~~~ 以下實例演示了變量在內存中地址: ~~~ package main import "fmt" func main() { var a int = 10 fmt.Printf("變量的地址: %x\n", &a) } ~~~ 執行以上代碼輸出結果為: ~~~ 變量的地址: c420012058 ~~~ 現在我們已經了解了什么是內存地址和如何去反問它。接下來我們將具體介紹指針。 什么是指針 一個指針變量可以指向任何一個值的內存地址,它指向那個值的內存地址。 類似于變量和常量,在使用指針前你需要聲明指針。 指針聲明格式如下: var name *類型 指針聲明: ~~~ package main var ip *int /* 指向整型*/ //聲明一個int值得指針變量 var fp *float32 /* 指向浮點型 */ var sp *string /* 指向字符串類型 */ func main() { } ~~~ 如何使用指針 指針使用流程: 定義指針變量。 為指針變量賦值。 訪問指針變量中指向地址的值。 在指針類型前面加上 * 號(前綴)來獲取指針所指向的內容。 ~~~ package main import "fmt" func main() { var a int = 20 /* 聲明實際變量 */ var ip *int /* 聲明指針變量 */ ip = &a /* 指針變量的存儲地址 */ fmt.Printf("a 變量的地址是: %x\n", &a) /* 指針變量的存儲地址 */ fmt.Printf("ip 變量的存儲地址: %x\n", ip) /* 使用指針訪問值 */ fmt.Printf("*ip 變量的值: %d\n", *ip) } ~~~ 輸出結果: ~~~ a 變量的地址是: c420012058 ip 變量的存儲地址: c420012058 *ip 變量的值: 20 ~~~ 直接用指針訪問目標對象成員: ~~~ package main import ( "fmt" ) func main() { type data struct{ a int } var d = data{1234} var p *data p = &d fmt.Printf("%p, %v\n", p, p.a) // 直接用指針訪問目標對象成員,無須轉換。 } ~~~ 輸出結果: ~~~ 0xc420012058, 1234 ~~~ 不能對指針做加減法等運算。 ~~~ package main func main() { x := 1234 p := &x p++ } ~~~ 輸出結果: ~~~ ./main.go:6:3: invalid operation: p++ (non-numeric type *int) ~~~ 可以在 unsafe.Pointer 和任意類型指針間進 轉換。 ~~~ package main import ( "fmt" "unsafe" ) func main() { x := 0x12345678 p := unsafe.Pointer(&x) // *int -> Pointer n := (*[4]byte)(p) // Pointer -> *[4]byte for i := 0; i < len(n); i++ { fmt.Printf("%X ", n[i]) } fmt.Println() } ~~~ 輸出結果: ~~~ 78 56 34 12 ~~~ 返回局部變量指針是安全的,編譯器會根據需要將其分配在 GC Heap 上。 ~~~ package main func main() {} func test() *int { x := 100 return &x // 在堆上分配 x 內存。但在內聯時,也可能直接分配在目標棧。 } ~~~ 將 Pointer 轉換成 uintptr,可變相實現指針運算。 ~~~ package main import ( "fmt" "unsafe" ) func main() { d := struct { s string x int }{"abc", 100} p := uintptr(unsafe.Pointer(&d)) // *struct -> Pointer -> uintptr p += unsafe.Offsetof(d.x) // uintptr + offset p2 := unsafe.Pointer(p) // uintptr -> Pointer px := (*int)(p2) // Pointer -> *int *px = 200 // d.x = 200 fmt.Printf("%#v\n", d) } ~~~ 輸出結果: ~~~ struct { s string; x int }{s:"abc", x:200} ~~~ 注意:GC 把 uintptr 當成普通整數對象,它無法阻止 "關聯" 對象被回收。 Go 空指針 (nil) 當一個指針被定義后沒有分配到任何變量時,它的值為 nil。 nil 指針也稱為空指針。 nil在概念上和其它語言的null、None、nil、NULL一樣,都指代零值或空值。 一個指針變量通常縮寫為 ptr。 實例: ~~~ package main import "fmt" func main() { var ptr *int fmt.Printf("ptr 的值為 : %x\n", ptr) } ~~~ 輸出結果: ~~~ ptr 的值為 : 0 ~~~ 空指針判斷: ~~~ package main import ( "fmt" ) func main() { var ptr1 *int var i int = 1 ptr2 := &i if ptr1 == nil { fmt.Println("prt1 是空指針") } if ptr2 != nil { fmt.Println("prt2 不是空指針") } } ~~~ 輸出結果: ~~~ prt1 是空指針 prt2 不是空指針 ~~~ Go 指針數組 在我們了解指針數組前,先看個實例,定義了長度為 3 的整型數組: ~~~ package main import "fmt" const MAX int = 3 func main() { a := [MAX]int{10, 100, 200} var i int for i = 0; i < MAX; i++ { fmt.Printf("a[%d] = %d\n", i, a[i]) } } ~~~ 輸出結果: ~~~ a[0] = 10 a[1] = 100 a[2] = 200 ~~~ 有一種情況,我們可能需要保存數組,這樣我們就需要使用到指針。 以下聲明了整型指針數組: ~~~ var ptr [MAX]*int; ~~~ ptr 為整型指針數組。因此每個元素都指向了一個值。以下實例的三個整數將存儲在指針數組中: ~~~ package main import "fmt" const MAX int = 3 func main() { a := []int{9, 99, 999} var i int var ptr [MAX]*int for i = 0; i < MAX; i++ { ptr[i] = &a[i] /* 整數地址賦值給指針數組 */ } for i = 0; i < MAX; i++ { fmt.Printf("a[%d] = %d\n", i, *ptr[i]) } } ~~~ 輸出結果: ~~~ a[0] = 9 a[1] = 99 a[2] = 999 ~~~ 如果一個指針變量存放的又是另一個指針變量的地址,則稱這個指針變量為指向指針的指針變量。 當定義一個指向指針的指針變量時,第一個指針存放第二個指針的地址,第二個指針存放變量的地址: 指向指針的指針變量聲明格式如下: ~~~ var ptr **int ~~~ 以上指向指針的指針變量為整型。 訪問指向指針的指針變量值需要使用兩個 * 號,如下所示: ~~~ package main import "fmt" func main() { var a int var ptr *int var pptr **int a = 3000 /* 指針 ptr 地址 */ ptr = &a /* 指向指針 ptr 地址 */ pptr = &ptr /* 獲取 pptr 的值 */ fmt.Printf("變量 a = %d\n", a) fmt.Printf("指針變量 *ptr = %d\n", *ptr) fmt.Printf("指向指針的指針變量 **pptr = %d\n", **pptr) } ~~~ 輸出結果: ~~~ 變量 a = 3000 指針變量 *ptr = 3000 指向指針的指針變量 **pptr = 3000 ~~~ Go 像函數傳遞指針參數 Go 語言允許向函數傳遞指針,志需要在函數定義的參數上設置為指針類型即可。 以下實例演示了如何向函數傳遞指針,并在函數調用后修改函數內的值,: ~~~ package main import "fmt" func main() { /* 定義局部變量 */ var a int = 100 var b int = 200 fmt.Printf("交換前 a 的值 : %d\n", a) fmt.Printf("交換前 b 的值 : %d\n", b) /* 調用函數用于交換值 * &a 指向 a 變量的地址 * &b 指向 b 變量的地址 */ swap(&a, &b) fmt.Printf("交換后 a 的值 : %d\n", a) fmt.Printf("交換后 b 的值 : %d\n", b) } func swap(x *int, y *int) { var temp int temp = *x /* 保存 x 地址的值 */ *x = *y /* 將 y 賦值給 x */ *y = temp /* 將 temp 賦值給 y */ } ~~~ 輸出結果: ~~~ 交換前 a 的值 : 100 交換前 b 的值 : 200 交換后 a 的值 : 200 交換后 b 的值 : 100 ~~~ 指針類型轉換 Go語言是不允許兩個指針類型進行轉換的。 我們一般使用*T作為一個指針類型,表示一個指向類型T變量的指針。為了安全的考慮,兩個不同的指針類型不能相互轉換,比如*int不能轉為*float64。 ~~~ package main import "fmt" func main() { i := 10 ip := &i var fp *float64 = (*float64)(ip) fmt.Println(fp) } ~~~ 輸出結果: ~~~ ./main.go:9:30: cannot convert ip (type *int) to type *float64 ~~~ 以上代碼我們在編譯的時候,會提示cannot convert ip (type *int) to type *float64,也就是不能進行強制轉型。那如果我們還是需要進行轉換怎么做呢?這就需要我們使用unsafe包里的Pointer了,下面我們先看看unsafe.Pointer是什么,然后再介紹如何轉換。 Pointer unsafe.Pointer是一種特殊意義的指針,它可以包含任意類型的地址,有點類似于C語言里的void*指針,全能型的。 ~~~ package main import ( "fmt" "unsafe" ) func main() { i := 10 ip := &i var fp *float64 = (*float64)(unsafe.Pointer(ip)) *fp = *fp * 3 fmt.Println(i) } ~~~ 輸出結果: ~~~ 30 ~~~ 以上示例,我們可以把*int轉為*float64,并且我們嘗試了對新的*float64進行操作,打印輸出i,就會發現i的址同樣被改變。 以上這個例子沒有任何實際的意義,但是我們說明了,通過unsafe.Pointer這個萬能的指針,我們可以在*T之間做任何轉換。 ~~~ type ArbitraryType int type Pointer *ArbitraryType ~~~ 可以看到unsafe.Pointer其實就是一個*int,一個通用型的指針。 我們看下關于unsafe.Pointer的4個規則。 任何指針都可以轉換為unsafe.Pointer unsafe.Pointer可以轉換為任何指針 uintptr可以轉換為unsafe.Pointer unsafe.Pointer可以轉換為uintptr 前面兩個規則我們剛剛已經演示了,主要用于*T1和*T2之間的轉換,那么最后兩個規則是做什么的呢?我們都知道*T是不能計算偏移量的,也不能進行計算,但是uintptr可以,所以我們可以把指針轉為uintptr再進行便宜計算,這樣我們就可以訪問特定的內存了,達到對不同的內存讀寫的目的。 下面我們以通過指針偏移修改Struct結構體內的字段為例,來演示uintptr的用法。 ~~~ package main import ( "fmt" "unsafe" ) func main() { u := new(user) fmt.Println(*u) pName := (*string)(unsafe.Pointer(u)) *pName = "張三" pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(u)) + unsafe.Offsetof(u.age))) *pAge = 20 fmt.Println(*u) } type user struct { name string age int } ~~~ 輸出結果: ~~~ { 0} {張三 20} ~~~ 以上我們通過內存偏移的方式,定位到我們需要操作的字段,然后改變他們的值。 第一個修改user的name值的時候,因為name是第一個字段,所以不用偏移,我們獲取user的指針,然后通過unsafe.Pointer轉為*string進行賦值操作即可。 第二個修改user的age值的時候,因為age不是第一個字段,所以我們需要內存偏移,內存偏移牽涉到的計算只能通過uintptr,所我們要先把user的指針地址轉為uintptr,然后我們再通過unsafe.Offsetof(u.age)獲取需要偏移的值,進行地址運算(+)偏移即可。 現在偏移后,地址已經是user的age字段了,如果要給它賦值,我們需要把uintptr轉為*int才可以。所以我們通過把uintptr轉為unsafe.Pointer,再轉為*int就可以操作了。 這里我們可以看到,我們第二個偏移的表達式非常長,但是也千萬不要把他們分段,不能像下面這樣。 ~~~ temp:=uintptr(unsafe.Pointer(u))+unsafe.Offsetof(u.age) pAge:=(*int)(unsafe.Pointer(temp)) *pAge = 20 ~~~ 邏輯上看,以上代碼不會有什么問題,但是這里會牽涉到GC,如果我們的這些臨時變量被GC,那么導致的內存操作就錯了,我們最終操作的,就不知道是哪塊內存了,會引起莫名其妙的問題。 小結 unsafe是不安全的,所以我們應該盡可能少的使用它,比如內存的操縱,這是繞過Go本身設計的安全機制的,不當的操作,可能會破壞一塊內存,而且這種問題非常不好定位。 當然必須的時候我們可以使用它,比如底層類型相同的數組之間的轉換;比如使用sync/atomic包中的一些函數時;還有訪問Struct的私有字段時;該用還是要用,不過一定要慎之又慎。 還有,整個unsafe包都是用于Go編譯器的,不用運行時,在我們編譯的時候,Go編譯器已經把他們都處理了。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看