- 数组、字符串和切片三者是密切相关的数据结构,因底层都是相同的结构。
- go中除了闭包函数以引用的方式对外部变量访问之外,其它赋值和函数传参数都是以传值的方式处理。
- 数组,需要指定固定长度,特殊类型,值类型,元素可修改,赋值和函数调用实质是以整体复制处理。不同长度或不同类型的数据组成的数组都是不同的类型,所以无法直接赋值。数组len和cap返回的数据是一样的。指针指向数组,和数组值,都可以用下标和range访问数组的值。
var a [ 3 ] int // 定义一个长度为3的int类 型数组, 元素全部为0 var b = [...] int { 1 , 2 , 3 } // 定义一个长度为3的 int类型数组, 元素为 1, 2, 3 var c = [...] int { 2 : 3 , 1 : 2 } // 定义一个长度为3 的int类型数组, 元素为 0, 2, 3 var d = [...] int { 1 , 2 , 4 : 5 , 6 } // 定义一个长度为 6的int类型数组, 元素为 1, 2, 0, 0, 5, 6
特殊:长度为0的数组在内存不会占用空间,可以用于强调某种特有类型的操作时避免分配额外的内存空间,比如用于管道的同步操作,chan上无需传输数据。
c1 := make ( chan [ 0 ] int ) go func () { fmt.Println( "c1" ) c1 <- [ 0 ] int {} }()
<- c1// 必须用key来初始化结构体,原因:结构体初始化有两种:1.指定字段名称。2.按顺序列出所有字段,不指定名称。如果使用2的方式进行初始化,假如别人重新定义了这个结构,中间加了字段,就会出现初始化不对的问题。protobuf也是这样做的。 type NoUnkeyedLiterals struct{} // 不允许结构体比较,只能针对到 ==,!=,>=,<=,>,<;如果使用反射relect.DeepEqual还是可以比较的。原理对于struct,只有全部字段是compareable才可以比较Slice、Map、Function均是不可比较的,只能判断是否为nil type DoNotCompare [0]func() // 不允许结构体拷贝、值传递,本质是 WaitGroup 内部维护了计数,不允许 copy 变量,内嵌 noCopy 字段,防止 Cond 变量被复制,还有sync.Mutex锁也是不允许copy的。 type DoNotCopy [0]sync.Mutex type User struct { Age int Address string //放在定义尾部更能节省空间padding // 必须用key来初始化结构体 NoUnkeyedLiterals // 不允许结构体比较 DoNotCompare // 不允许结构体拷贝、值传递 DoNotCopy } func main() { _ = &User{Age: 21, Address: "beijing"} fmt.Println(unsafe.Sizeof(NoUnkeyedLiterals{})) fmt.Printf("%p\n", &DoNotCopy{}) fmt.Printf("%p\n", &NoUnkeyedLiterals{}) fmt.Printf("%p\n", &DoNotCompare{}) }
package main import ( "sync" ) func test(wg sync.WaitGroup) { defer wg.Done() wg.Add(1) } // 经典WaitGroup变量死锁 func main() { var wg sync.WaitGroup wg.Add(1) go test(wg) wg.Wait() }
拓展:与零长度类似,空结构体也是可以用来节省内存,具体场景有:
- 将map作为Set使用时,可以将值类型定义为空结构体,仅作为占位符使用;
- channel不需要发送数据,只用来通知子协程执行任务等时,可使用空结构体作为占位符;
- 结构体只包含方法,不包含任何的字段时,可声明空结构体,以节省资源。
- 字符串,底层为byte数组,只读禁止了对底层字节数组的修改。字符串赋值只是复制了数据地址和对应的长度,而不会导致底层数据的复制。赋值操作也就是 reflect.StringHeader 结构体的复制过程,reflect.StringHeader包含两个类型。
type StringHeader struct { Data uintptr Len int }
- 字符串是支持转为[]rune和[]byte,rune是int32的别称。 字符串相关的强制类型转换主要涉及到 []byte 和 []rune 两 种类型。每个转换都可能隐含重新分配内存的代价,最坏的情 况下它们的运算时间复杂度都是 O(n)。因为 []byte 和 []int32 类型是完全不同的内部布局,所以并不知道一个字符串的底层到底是什么数组实现,就会存在重新分配内存的问题。
- 切片