一个字符串在内存中究竟什么样子? C语言字符串是一个字节数组加一个结束符。Go语言字符串标准结构是指针和长度,指针指向一个字节数组。
比如说多个人使用同一个字符串的时候或者字符串在不同函数中传递的时候,希望要么复制、要么不可变。这样的好处是不管引用多少次,只需要保留一份副本。当对其进行修改的时候就创建一个新的。
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。数组的长度是数组类型的组成部分。Go语言中数组是值语义,一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如C语言的数组),而是一个完整的值。当一个数组变量被赋值或者被传递的时候,实际上会复制整个数组。如果数组较大的话,数组的赋值也会有较大的开销。为了避免复制数组带来的开销,可以传递一个指向数组的指针,但是数组指针并不是数组。
切片是一个很简单的对数组进行管理的数据结构,但是它本身并不是数组,它通过一个指针引用一个数组,本身显然不是数组,是一个结构体。所以我们返回切片类型大小的时候,sizeof(type)实际上是三个字段相加的结果而不是它引用数组的长度。
Go语言中数组、字符串和切片三者是密切相关的数据结构。这三种数据类型,在底层有着相同的内存结构,在上层,因为语法的限制而有着不同的行为表现。首先,Go语言的数组是一种值类型,虽然数组的元素可以被修改,但是数组本身的赋值和函数传参都是以整体复制的方式处理的。Go语言字符串底层也是对应的字节数组,但是字符串的只读属性禁止了在程序对底层字节数组元素的修改。字符串赋值只是复制了数据地址和对应的长度,而不会导致底层数据的复制。切片的行为更为灵活,切片的结构和字符串结构类似,但是解除了只读限制。切片的底层数据虽然也是对应数据类型的数组,但是每个切片还有独立的长度和容量信息,切片赋值和函数传参数时也是将切片头信息部分按传值方式处理。因为切片头含有底层数据的指针,所以它的赋值也不会导致底层数据的复制。