• Golang string 源码


    它是一种基本类型,并且是一个不可改变的UTF-8字符序列

    回过来看 GO 里面的字符串,字符也是根据英文和中文不一样,一个字符所占用的字节数也是不一样的,大体分为如下 2 种

    • 英文的字符,按照ASCII 码来算,占用 1 个字节
    • 其他的字符,包括中文字符在内的,根据不同字符,占用字节数是 2 -- 4个字节

    字符串的数据结构是啥样的?

    说到字符串的数据结构,我们先来看看 GO 里面的字符串,是在哪个包里面

    不难发现,我们随便在 GOLANG 里面 定义个string 变量,就能够知道 string 类型是在哪个包里面,例如

    var name  string

    GO 里面的字符串对应的包是 builtin

    1 // string is the set of all strings of 8-bit bytes, conventionally but not
    2 // necessarily representing UTF-8-encoded text. A string may be empty, but
    3 // not nil. Values of string type are immutable.
    4 type string string
    • 字符串这个类型,是所有8-bits 字符串的集合,通常但不一定表示utf -8编码的文本
    • 字符串可以为空,但不能为 nil ,此处的字符串为空是 ""
    • 字符串类型的值是不可变的

    另外,找到 string 在 GO 里面对应的源码文件中src/runtime/string.go , 有这么一个结构体,只提供给包内使用,我们可以看到string的数据结构 stringStruct 是这个样子的

    1 type stringStruct struct {
    2     str unsafe.Pointer
    3     len int
    4 }

    整个结构体,就 2 个成员,string 类型是不是很简单呢

    • str

    是对应到字符串的首地址

    • len

    这个就是不难理解,是字符串的长度

    那么,在创建一个字符串变量的时候,stringStruct 是在哪里使用到的呢?

    我们看看 GO string.go 文件中的源码

     1 //go:nosplit
     2 func gostringnocopy(str *byte) string {
     3    ss := stringStruct{str: unsafe.Pointer(str), len: findnull(str)}  // 构建成 stringStruct
     4    s := *(*string)(unsafe.Pointer(&ss))  // 强转成 string
     5    return s
     6 }
     7 //go:nosplit
     8 func findnull(s *byte) int {
     9    if s == nil {
    10       return 0
    11    }
    12 
    13    // Avoid IndexByteString on Plan 9 because it uses SSE instructions
    14    // on x86 machines, and those are classified as floating point instructions,
    15    // which are illegal in a note handler.
    16    if GOOS == "plan9" {
    17       p := (*[maxAlloc/2 - 1]byte)(unsafe.Pointer(s))
    18       l := 0
    19       for p[l] != 0 {
    20          l++
    21       }
    22       return l
    23    }
    24 
    25    // pageSize is the unit we scan at a time looking for NULL.
    26    // It must be the minimum page size for any architecture Go
    27    // runs on. It's okay (just a minor performance loss) if the
    28    // actual system page size is larger than this value.
    29    const pageSize = 4096
    30 
    31    offset := 0
    32    ptr := unsafe.Pointer(s)
    33    // IndexByteString uses wide reads, so we need to be careful
    34    // with page boundaries. Call IndexByteString on
    35    // [ptr, endOfPage) interval.
    36    safeLen := int(pageSize - uintptr(ptr)%pageSize)
    37 
    38    for {
    39       t := *(*string)(unsafe.Pointer(&stringStruct{ptr, safeLen}))
    40       // Check one page at a time.
    41       if i := bytealg.IndexByteString(t, 0); i != -1 {
    42          return offset + i
    43       }
    44       // Move to next page
    45       ptr = unsafe.Pointer(uintptr(ptr) + uintptr(safeLen))
    46       offset += safeLen
    47       safeLen = pageSize
    48    }
    49 }

    简单分为 2 步:

    • 先将字符数据构建程 stringStruct
    • 再通过 gostringnocopy 函数 转换成 string

    字符串中的数据为什么不能被修改呢?

    从上述官方说明中,我们可以看到,字符串类型的值是不可变的

    可是这是为啥呢?

    我们以前在写C/C++的时候,为啥可以开辟空间存放多个字符,并且还可以修改其中的某些字符呢?

    可是在 C/C++里面的字面量也是不可以改变的

    GO 里面的 string 类型,是不是也和 字面量一样的呢?我们来看看吧

    字符串类型,本身也是拥有对应的内存空间的,那么修改string类型的值应该是要支持的。

    可是,XDM 在 Go 的实现中,string 类型是不包含内存空间,只有一个内存的指针,这里就有点想C/C++里面的案例:

    char * str = "XMTONG"

    上述的 str是绝对不能做修改的,str只是作为可读,不能写的。

    在GO 里面的字符串,就与上述类似。这样做的好处是 string 变得非常轻量,可以很方便的进行传递而不用担心内存拷贝(这也避免了内存带来的诸多问题)

    GO 中的 string类型一般是指向字符串字面量,字符串字面量存储位置是在虚拟内存分区的只读段上面,而不是堆或栈上。因此,GO 的 string 类型不可修改的。

    you are the best!
  • 相关阅读:
    理解盒子模型
    Jackson 框架,轻易转换JSON
    JAVA仿百度分页
    最干净,最便捷的卸载Mysql
    Mysql 6.0安装过程(截图放不上去)
    开发JSP自定义标签
    JAVA实现文件上传
    开发过程中常用工具类
    JQUERY 简单易用的提示框插件
    什么是Bash Shell的内建(build in)命令
  • 原文地址:https://www.cnblogs.com/linguoguo/p/15490197.html
Copyright © 2020-2023  润新知