go語言 string資料型別格式

weixin_34120274發表於2017-08-31

在go語言裡面定義字串如下:
var ss string = "12345"

對於一個熟悉C/C++的程式設計師來說,馬上就會想到這個string是一個什麼內容,佔多大空間,記憶體如何分配等,下面我們來分析這個問題。

go語言的string是一種資料型別,這個資料型別佔用16位元組空間,前8位元組是一個指標,指向字串值的地址,後八個位元組是一個整數,標識字串的長度;注意go語言的字串內部並不以'\0'作為結尾,而是通過一個長度域來表示字串的長度。

type mystr struct {
    strbuf uintptr;
    strlen uint64;
}

上述就是string的型別定義。下面我們通過程式碼來驗證這個問題:

package main

import (
    "fmt"
    "unsafe"
//  "reflect"
)

type mystr struct {
    strbuf uintptr;
    strlen uint64;
}

func printmemory(p uintptr, size int) {
    fmt.Printf("[0x%16x:%2d] =", p, size)
    for i := 0; i < size; i++ {
        p1 := unsafe.Pointer(p + uintptr(i))
        p2 := (*byte)(unsafe.Pointer(p1))
        fmt.Printf(" %x", *p2)
    }
    fmt.Printf("\n")
}

func main() {
    var ss string = "12345";

    fmt.Printf("string=%v\n", ss)
    fmt.Printf("length=%v\n", len(ss))
    fmt.Printf("size=%v\n", unsafe.Sizeof(ss))
    fmt.Printf("address=%v\n", &ss)

    ptr := unsafe.Pointer(&ss)
    //value := reflect.ValueOf(&ss)
    //fmt.Println(reflect.TypeOf(value), reflect.ValueOf(value).Kind())
    //ptr1 := value.Pointer()
    ptr1 := uintptr(ptr)
    printmemory(ptr1, 16);

    ptr2 := (* mystr)(ptr)
    fmt.Printf("mystr.strbuf=%x\n", ptr2.strbuf)
    fmt.Printf("mystr.strlen=%v\n", ptr2.strlen)
    printmemory(ptr2.strbuf, len(ss)+1);
}

執行結果如下:

$ go build main.go && ./main 
string=12345
length=5
size=16
address=0xc42000e2c0
[0x      c42000e2c0:16] = f1 70 4a 0 0 0 0 0 5 0 0 0 0 0 0 0
mystr.strbuf=4a70f1
mystr.strlen=5
[0x          4a70f1: 6] = 31 32 33 34 35 31

我們看到string的記憶體結構,包含一個指向字串資料的指標,和一個標識字串長度的整數值;而且字串的結尾並沒有一個'\0'來標識,在上述例子中是一個隨機值(0x31)。

關於go語言的指標操作

go語言指標和C/C++指標的唯一差別就是:go語言不允許對指標做算術運算(+、-、++、--)。

但是,Go 提供了一套底層庫 reflect 和 unsafe,它們可以把任意一個 go 指標轉成 uintptr型別的值,然後再像 C/C++一樣對指標做算術運算,最後再還原成 go 型別。所以從這個角度上看,go 指標也是可以和 C/C++ 指標一樣使用的,只是會比較繞,這同時也要求使用者自己明白,如果真要把指標這麼用,那麼請記得後果自負。

下面是一個go語言指標運算的例子:
https://play.golang.org/p/z_GMnh38Z1

相關文章