第四章 复合类型
< P83 >
- 长度亦算数组类型的一部分因此两个长度不同的数组是不同类型;
- 只有两个数组的元素类型可以比较时,这两个数组才可以比较;
- 调用函数传值时,会将数组拷贝传递(传值引用),所以在函数内的修改对原数组无影响;
< P86 >
- 数组和切片声明时可以显式声明index,当两者混用时,隐式声明的元素index随前方相邻的显式声明index自增;
与数组不同,切片不能直接用
==
或!=
比较,只有与nil
的比较是合法的,除了[]byte
可以用bytes.Equal
方法比较外,其它类型切片我们都要自行逐个元素比较;Go语言核心之美 3.2-slice切片
为什么Go语言不支持slice
的比较运算呢?- 第一个原因,
slice
是引用类型,一个slice
甚至可以引用自身。虽然有很多解决办法,但是没有一个是简单有效的; - 第二个原因,因为
slice
是间接引用,因此一个slice
在不同时间可能包含不同的元素-底层数组的元素可能被修改; - 只要一个数据类型可以做相等比较,那么就可以用来做
map
的key,map
这种数据结构对key的要求是:如果最开始时key是相等的,那在map
的生命周期内,key要一直相等,因此这里key是不可变的。而对于指针或chan
这类引用类型,==
可以判断两个指针是否引用了想同的对象,是有用的,但是slice
的相等测试充满了不确定性,因此,安全的做法是禁止slice
之间的比较操作。
- 第一个原因,
可以存在
len
和cap
都为0
但是non-nil的slice
,所以判定slice
是否为空应该用len
;1
2
3
4var s []int // len(s) == 0, s == nil
s = nil // len(s) == 0, s == nil
s = []int(nil) // len(s) == 0, s == nil
s = []int{} // len(s) == 0, s != nil
< P94 >
map
的键应该可以用==
比较,不建议用浮点数作键;map
中的元素不是变量,因此不能被取地址;map
可能会随着元素的增多重新分配更大的内存空间,旧值都会拷贝到新的内存空间,因此之前的地址就会失效。和切片一样,
map
间不能直接比较,只有和nil
比较是合法的;
< P100 >
结构体的字段(field)可以取地址并通过指针访问:
1 | func EmployeeByID(id int) *Employee { /* ... */ } |
最后那条语句需要注意,它调用EmployeeByID
生成了一个*Employee
指针,然后直接更新该结构体中的一个字段。如果将EmployeeByID
的返回值从*Employee
换成Employee
类型,那么编译将报错,因为编译器无法对返回的Employee
进行寻址(不通过变量直接使用一个值,一般都无法进行寻址)。
< P101 >
对于结构体来说,字段顺序很重要,如果顺序不同或者有一些字段合并声明,即使字段名完全相同,那也算两个完全不同的结构体类型;
< P104 >
当结构体中所有字段都可以比较时,这个结构体类型才可以相比较;
< P106 >
准确来说,结构体中的匿名字段并不是真正的匿名,有隐式的与类型名相同的名字;在格式化字符串中加入
#
,#
代表用Go的语法来打印。对于struct
类型来说,打印中将包含每个字段的信息;1
2
3
4
5
6
7
8
9
10
11w = Wheel{
Circle: Circle{
Point: Point{X: 8, Y: 8},
Radius: 5,
},
Spokes: 20, // NOTE: trailing comma necessary here (and at Radius)
} // or w = Wheel{Circle{Point{8, 8}, 5}, 20}
fmt.Printf("%#v\n", w)
// Output:
// Wheel{Circle:Circle{Point:Point{X:8, Y:8}, Radius:5}, Spokes:20}当匿名字段没有导出时,不能用长赋值形式;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// mStruct.go
package mStruct
type point struct {
X, Y int
}
type Cyc struct {
point
}
// main.go
package main
func main() {
var m = mStruct.Cyc{}
m.X = 1
m.point.Y = 2 // cannot refer to unexported field or method point
fmt.Printf("%#v\n", m) // mStruct.Cyc{point:mStruct.point{X:1, Y:0}}
}