一,區(qū)別
1岩齿,指針類型
golang支持指針類型,指針類型的變量存的是一個內存地址苞俘,這個地址指向的內存空間存的才是一個具體的值盹沈。
比如int,int32吃谣,A(自定義結構體類型)乞封,string等,都是指針類型岗憋。
golang的指針類型和c/c++的指針類型基本一樣肃晚,但是多了幾個限制:
- 1,int仔戈,int32等不同的指針類型不能相互轉化.
- 2关串,指針類型不支持c/c++這樣的指針運算。
2监徘,unsafe.Pointer類型
這個類型比較重要晋修,它是實現(xiàn)定位和讀寫的內存的基礎。go runtime大量使用它凰盔。官方解釋是:
Pointer represents a pointer to an arbitrary type. There are four special operations available for type Pointer that are not available for other types:
- A pointer value of any type can be converted to a Pointer.
- A Pointer can be converted to a pointer value of any type.
- A uintptr can be converted to a Pointer.
- A Pointer can be converted to a uintptr.
根據(jù)官方解釋墓卦,unsafe.Pointer類型可以指代上面說的任意一種指針類型。
- unsafe.Pointer類型可以和任意指針類型互轉
- unsafe.Pointer類型可以和uintptr類型(后面介紹)互轉
3, uintprt類型
uintptr是golang的內置類型户敬。是能存儲指針的整形落剪。
type uintptr uintptr
在64位平臺上,底層的數(shù)據(jù)類型是:
typedef unsigned long long int uint64;
typedef uint64 uintptr;
uintptr的用處也很大:
- uintptr可以和unsafe.Pointer類型互轉(上面提到過)尿庐。
- uintptr可以做指針運算忠怖,這一點有時候很重要,但是依賴平臺屁倔,同一類型變量在不同的平臺占用的存儲空間大小不一樣脑又,在用uintptr做指針運算的時候,偏移量也會相應的不一樣(后面有例子說明)。
二问麸,使用
上面介紹了指針往衷,unsafe.Pointer和uintptr這三種類型,看起來這三種類型存的都是變量的內存地址严卖,那為什么要有三種呢席舍?關鍵就在于:
- unsafe.Pointer類型可以和另外兩種類型互轉
- uintptr類型可以做指針運算
- 指針類型可以方便的取變量值
把這三者結合起來,我們就可以方便的對變量做修改:
type MyStruct struct {
i int
j int
}
func myFunction(ms *MyStruct) {
ptr := unsafe.Pointer(ms)
for i := 0; i < 2; i++ {
c := (*int)(unsafe.Pointer((uintptr(ptr) + uintptr(8*i))))
*c += i + 1
fmt.Printf("[%p] %d\n", c, *c)
}
}
func main() {
a := &MyStruct{i: 40, j: 50}
myFunction(a)
fmt.Printf("[%p] %v\n", a, a)
}
這個例子是分析golang結構體在內存中的布局寫的哮笆,*MyStruct是指針類型来颤,我們想對i和j兩個成員的值做修改。
為了拿到j的內存地址稠肘,先用uintptr(unsafe.Pointer(*MyStruct))來獲取i的內存地址福铅,然后用uintptr+8這種地址運算來得到成員j的內存地址,這要基于兩點保證:
- 1项阴,golang的struct結構體數(shù)據(jù)在內存中是一整塊連續(xù)的滑黔。
- 2,+8是int類型在64位操作系統(tǒng)上占用8字節(jié)环揽,同樣的類型在不同的操作系統(tǒng)上占用內存空間是不一樣的略荡。
從這個例子我們可以看到unsafe.Pointer和uintptr和指針的配合使用可以給我們對運行時的代碼做一些非常靈活的操作。