1.Array
数组或者列表属于值类型,赋值和传参会赋值整个数组,而不是指针
看一组数据区别,
共同点:地址都发生了变化,因此在进行传参时会进行复制,
不同点:如果数组指定长度(这个叫数组),那么在进行调用的时候并赋值的时候,原数据不变
如果未指定长度(这个叫切片),那么在进行复制的时候,就会把原数组的值得地址进行传递,这样在改变值地址对应的值时,原数组对应的值也就会发生变化。
func test(x []int){ fmt.Printf("test: %p ", &x) fmt.Printf("test 值: %p ", x) x[1] = 10 } func main() { var s = []int{1,2,3} test(s) fmt.Println(s) fmt.Printf("main: %p ", &s) fmt.Printf("main 值: %p ", s) }
test: 0xc000096020 test 值: 0xc000098000 [1 10 3] main: 0xc000096000 main 值: 0xc000098000
func test(x [3]int){ fmt.Printf("test: %p ", &x) fmt.Printf("test 值: %p ", x) x[1] = 10 } func main() { var s = [3]int{1,2,3} test(s) fmt.Println(s) fmt.Printf("main: %p ", &s) fmt.Printf("main 值: %p ", s) }
test: 0xc000096020 test 值: %!p([3]int=[1 2 3]) [1 2 3] main: 0xc000096000 main 值: %!p([3]int=[1 2 3]
使用len以及cap都可以计算数组长度
2.切片
len代表切片的可用长度,cap代表切片的最大扩张容量,因此len() <= cap(),读写操作不能超过len,否则会报错,cap的作用是在对切片扩容时,来为切片进行分配空间大小
如第一篇讲到,可以使用make以及new来创建新切片
s := make([]int, 6. 8) // len 6 cap 8 s := make([]int, 6) // len 6 cap 6
当对数组进行切片时,它是用过内部指针以和相关属性引用的数组片段,因此改变切片,会对数组造成相应的影响
一个切片是一个数组片段的描述。它包含了指向数组的指针,片段的长度, 和容量(片段的最大长度)。

使用 make([]byte, 5)
创建的切片变量 s
的结构如下:

长度是切片引用的元素数目。容量是底层数组的元素数目(从切片指针开始)
从上述图片可以看出,切片就是引用原来的数组片段,因此对切片再次进行切片,依然会影响原数组
func main() { var s = [10]int{1,2,3,4,5,6,7,8,9,0} a := s[1:4] b := a[1:2] b[0] = 1000 fmt.Println(s) // [1 2 1000 4 5 6 7 8 9 0] }
这个就是对切片扩容时,因为cap的长度满足扩容大小,因此原数组发生变化,但是如果超出cap限制,底层数组就会重新分配,原数组不会受到影响
func main() {
var data = [10]int{1,2,3,4,5,6,7,8,9,0}
s := data[1:3]
fmt.Println(cap(s), len(s)) // 9 2
s2 := append(s, 100, 200,300,400,500,600,700) // 添加多个值。
fmt.Println(data) // [1 2 3 100 200 300 400 500 600 700]
fmt.Println(s) // [2 3]
fmt.Println(s2) // [2 3 100 200 300 400 500 600 700]
fmt.Println(&s2[2], &data[3]) // 0xc000022108 0xc000022108
s3 := data[1:2:3]
fmt.Println(cap(s3), len(s3)) // 2 1
s4 := append(s3, 1000,2000,3000)
fmt.Println(data) // [1 2 3 100 200 300 400 500 600 700]
fmt.Println(s3) // [2]
fmt.Println(s4) // [2 1000 2000 3000]
fmt.Println(&s4[0], &data[1]) // 0xc00008c020 0xc000082008
}
3.map
哈希表,其实就是python中的字典
键必须是支持相等运算符的类型,例如number,string,pointer,array,struct,interface,值可以是任意
可以使用v,ok := m[键] 来判断是否存在该值。
另外,如果m[不存在的键]不会报错,直接返回空值,删除不存在的也不会报错
map的长度可以使用len测量
for range循环时,可以只取键或者键跟值,每次取的顺序都是随机的
func main() {
m := map[[1]int]string{
[1]int{1} : "s",
[1]int{3} : "sss",
[1]int{4} : "ssss",
[1]int{5} : "sssss",
[1]int{6} : "ssssss",
[1]int{7} : "sssssss",
}
fmt.Println(m) // map[[1]:s [3]:sss [4]:ssss [5]:sssss [6]:ssssss [7]:sssssss]
if v,ok := m[[1]int{1}]; ok {
fmt.Println(v, ok) // s true
}
fmt.Println(m[[1]int{2}]) // ""
fmt.Println(len(m)) // 6
for key, value := range m {
fmt.Println(key, value) // 随机顺序返回,每次不相同
}
for key := range m {
fmt.Println(key) // 随机顺序返回key也就是键,每次不相同
}
}
4.struct
如同前文所说,定义结构体
结构体支持相等操作符,支持匿名结构
type 类型名 struct {} 建议类型名首字母大写,在对struct进行顺序赋值的时候,必须包含全部字段,否则会报错,但是使用关键字进行赋值,则没问题,未赋值的默认为空值
func main() { type Mm struct { Id int Id2 int } m := Mm{Id:1} fmt.Println(m) // {1 0} }
func main() { type File struct { name string size int attr struct { perm int owner int } } f := File{ name: "test.txt", size: 1025, } f.attr.owner = 1 f.attr.perm = 755 fmt.Println(f) // {test.txt 1025 {755 1}} var attr = struct { perm int owner int }{2, 2222} f.attr = attr fmt.Println(f) // {test.txt 1025 {2 2222}} }
结构体中的匿名字段,一般对于结构体来讲,如果变量名称与类型名称同名,则可以省略类型名称,算是一个语法糖
可以像访问普通字段一样访问匿名成员,编译器会从外部逐层去找所有层次的匿名字段,因此a.id其实就是a.User.id,但是同层次只可以出现一个这样的字段,否则编译器就会凌乱,然后报错,因此一般如果分不清建议使用显示的字段名。
外层同名字段会遮蔽潜入字段名
type User struct { id int } type Admin struct { User name string } a := Admin{} a.id =1 a.name = "google" fmt.Println(a) // {{1} google}
func main() {
type User struct {
id int
}
type Admin struct {
User
id int
name string
}
a := Admin{}
a.id =1
a.name = "google"
fmt.Println(a) // {{0} 1 google}
a.User.id = 2
fmt.Println(a) // {{2} 1 google}
}