Golang---基本類型(string)


    摘要:由於在實習過程中,做的項目都是基於 Golang 語言,所以在面試時,面試官也一定會理所當然的問 Golang, 所以在最近一段時間,主要學習這門語言的基礎知識,以及常出的面試題。

 

簡單介紹

  字符串雖然在 Go 語言中是基本類型 string, 但是它實際上是由字符組成的數組,類似於 C 語言中的  char [] ,作為數組會占用一片連續的內存空間。Go 語言中的字符串其實只是一個只讀的字節數組,不支持直接修改 string 類型變量的內存空間,比如下面代碼就是不支持的:

package main

import (
    "fmt"
)

func main() {
    s := "hello"
    s[0] = 'A'

    fmt.Println(s)
}

//.\main.go:9:7: cannot assign to s[0]
err-example1

  如果我們想修改字符串,我們可以將這段內存拷貝到堆或者棧上,將遍歷的類型轉換為 []byte 之后就可以進行,修改后通過類型轉換就可以變回 string, 對原變量重新賦值即可。

package main

import (
    "fmt"
)

func main() {
    s := "hello"
    sByte := []byte(s)
    sByte[0] = 'A'

    //重新賦值
    s = string(sByte)

    fmt.Println(s)
}

//Aello
right-example1

 

數據結構

    字符串在 Go 語言中的接口其實非常簡單,每一個字符串在運行時都會使用如下的 StringHeader 結構體表示,其實在”運行時“內部,有一個私有的結構 stringHeader, 它有着完全相同的結構,只是用於存儲數據的 Data 字段使用了 unsafe.Pointer 類型:

// StringHeader is the runtime representation of a string.
// It cannot be used safely or portably and its representation may
// change in a later release.
// Moreover, the Data field is not sufficient to guarantee the data
// it references will not be garbage collected, so programs must keep
// a separate, correctly typed pointer to the underlying data.
type StringHeader struct {
    Data uintptr
    Len  int
}

// stringHeader is a safe version of StringHeader used within this package.
type stringHeader struct {
    Data unsafe.Pointer
    Len  int
}
string-struct

 

聲明方式

   使用雙引號

s := "hello world"

   使用反引號

s := `hello world`

使用雙引號可其它語言沒有什么大的區別,如果字符串內部出現雙引號,要使用 \ 進行轉義;但使用反引號則不需要,方便進行更加復雜的數據類型,比如 Json:

s := `{"name": "sween", "age": 18}`

注:上面兩種格式的解析函數分別為cmd/compile/internal/syntax.scanner.stdString

cmd/compile/internal/syntax.scanner.rawString

 

類型轉換

    在我們使用 Go 語言解析和序列化 Json 等數據格式時,經常需要將數據在 string 和 []byte 之間進行轉換,類型轉換的開銷其實並沒有想象中的那么小。

[]byte 到 string 的轉換

runtime.slicebytetostring 這個函數中進行轉換的處理,我們看下源碼:

// slicebytetostring converts a byte slice to a string.
// It is inserted by the compiler into generated code.
// ptr is a pointer to the first element of the slice;
// n is the length of the slice.
// Buf is a fixed-size buffer for the result,
// it is not nil if the result does not escape.
func slicebytetostring(buf *tmpBuf, ptr *byte, n int) (str string) {
    if n == 0 {
        // Turns out to be a relatively common case.
        // Consider that you want to parse out data between parens in "foo()bar",
        // you find the indices and convert the subslice to string.
        return ""
    }
    if n == 1 {
        p := unsafe.Pointer(&staticuint64s[*ptr])
        if sys.BigEndian {
            p = add(p, 7)
        }
        stringStructOf(&str).str = p
        stringStructOf(&str).len = 1
        return
    }

    var p unsafe.Pointer
    if buf != nil && n <= len(buf) {
        p = unsafe.Pointer(buf)
    } else {
                //step1: 分配內存空間
        p = mallocgc(uintptr(n), nil, false)
    }
    stringStructOf(&str).str = p
    stringStructOf(&str).len = n
        //step2:執行內存拷貝操作
    memmove(p, unsafe.Pointer(ptr), uintptr(n))
    return
}
[]byte 轉 string 源碼

string 到 []byte 的轉換

runtime.stringtoslicebyte 這個函數中進行轉換的處理,我們看下源碼:

func stringtoslicebyte(buf *tmpBuf, s string) []byte {
    var b []byte
    if buf != nil && len(s) <= len(buf) {
                //step1: 如果緩沖區夠用,直接用
        *buf = tmpBuf{}
        b = buf[:len(s)]
    } else {
                //step2: 如果緩沖區不夠用,重新分配一個
        b = rawbyteslice(len(s))
    }
        //step3: 執行內存拷貝操作
    copy(b, s)
    return b
}

// rawbyteslice allocates a new byte slice. The byte slice is not zeroed.
func rawbyteslice(size int) (b []byte) {
    cap := roundupsize(uintptr(size))
    p := mallocgc(cap, nil, false)
    if cap != uintptr(size) {
        memclrNoHeapPointers(add(p, uintptr(size)), cap-uintptr(size))
    }

    *(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)}
    return
}
string 轉 []byte 源碼

 

總結

    字符串和 []byte 中的內容雖然一樣,但是字符串的內容是只讀的,我們不能通過下標或者其它形式改變其中的數據,而 []byte 中的內容是可讀寫的,無論哪種類型轉換到另一種類型都需要對其中的內容進行拷貝,而內存拷貝的性能損耗會隨着字符串和 []byte 長度的增長而增長。所以在做類型轉換時候一定要注意性能的損耗。

 

參考資料:

https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-string/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM