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/

 

posted on 2020-09-02 14:54  爱笑的张飞  阅读(2200)  评论(0编辑  收藏  举报

导航