一、数组、字符串、切片
1、数组定义方式:
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
Go语言中数组是值语义。一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如C语言的数组),而是一个完整的值。为了避免复制数组带来的开销,可以传递一个指向数组的指针,但是数组指针并不是数组。
用for
循环来迭代数组:
for i := range a {
fmt.Printf("a[%d]: %d\n", i, a[i])
}
for i, v := range b {
fmt.Printf("b[%d]: %d\n", i, v)
}
for i := 0; i < len(c); i++ {
fmt.Printf("c[%d]: %d\n", i, c[i])
}
var times [5][0]int
for range times {
fmt.Println("hello")
}
其中times
对应一个[5][0]int
类型的数组,虽然第一维数组有长度,但是数组的元素[0]int
大小是0,因此整个数组占用的内存大小依然是0。没有付出额外的内存代价,我们就通过for range
方式实现了times
次快速迭代。
空的数组定义:
var d [0]int // 定义一个长度为0的数组
var e = [0]int{} // 定义一个长度为0的数组
var f = [...]int{} // 定义一个长度为0的数组
长度为0的数组在内存中并不占用空间。
管道接收和发送操作只是用于消息的同步:
c2 := make(chan struct{})
go func() {
fmt.Println("c2")
c2 <- struct{}{} // struct{}部分是类型, {}表示对应的结构体值
}()
<-c2
可以用fmt.Printf
函数提供的%T
或%#v
谓词语法来打印数组的类型和详细信息:
fmt.Printf("b: %T\n", b) // b: [3]int
fmt.Printf("b: %#v\n", b) // b: [3]int{1, 2, 3}
2、字符串的元素不可修改,是一个只读的字节数组。
Go语言的源代码要求是UTF8编码,源代码中的文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列。
for range
等语法并不能支持非UTF8编码的字符串的遍历。
Go语言字符串的底层结构在reflect.StringHeader
中定义:
type StringHeader struct {
Data uintptr
Len int
}
字符串结构由两个信息组成:第一个是字符串指向的底层字节数组,第二个是字符串的字节的长度。
rune
只是int32
类型的别名,并不是重新定义的类型。rune
用于表示每个Unicode码点,目前只使用了21个bit位。
3、切片的定义:
内置的len
函数返回切片中有效元素的长度,内置的cap
函数返回切片容量大小,容量必须大于或等于切片的长度。
在开头一般都会导致内存的重新分配,而且会导致已有的元素全部复制1次。因此,从切片的开头添加元素的性能一般要比从尾部追加元素的性能差很多。
切片中间插入元素:
var a []int
a = append(a[:i], append([]int{x}, a[i:]...)...) // 在第i个位置插入x
a = append(a[:i], append([]int{1,2,3}, a[i:]...)...) // 在第i个位置插入切片
如果len
和cap
都为0
的话,则变成一个真正的空切片,虽然它并不是一个nil
值的切片。在判断一个切片是否为空时,一般通过len
获取切片的长度来判断,一般很少将切片和nil
值做直接的比较。
Go语言实现中非0大小数组的长度不得超过2GB,因此需要针对数组元素的类型大小计算数组的最大长度范围([]uint8
最大2GB,[]uint16
最大1GB,以此类推,但是[]struct{}
数组的长度可以超过2GB)。
二、函数
如果返回值命名了,可以通过名字来修改返回值,也可以通过defer
语句在return
语句之后修改返回值:
func Inc() (v int) {
defer func(){ v++ } ()
return 42
}
其中defer
语句延迟执行了一个匿名函数,因为这个匿名函数捕获了外部函数的局部变量v
,这种函数我们一般叫闭包。闭包对捕获的外部变量并不是传值方式访问,而是以引用的方式访问