1.1 什么是结构体

结构体也就是类似java中的类,是一种由一系列具有相同类型或不同类型的数据构成的数据集合

1.2 结构体定义

  1. type struct_variable_type struct {
  2. member definition;
  3. member definition;
  4. ...
  5. member definition;
  6. }
  7. // 一旦定义了结构体类型,它就能用于变量的声明
  8. variable_name := structure_variable_type {value1, value2...valuen}

示例:

  1. type User struct {
  2. name string
  3. age int
  4. sex byte
  5. }
  6. func main() {
  7. // 按照顺序进行初始化
  8. p1 := User{"ada", 19, 'F'}
  9. fmt.Println(p1)
  10. // 按照field:value进行初始化,这样可以任意顺序
  11. p2 := User{
  12. age: 24,
  13. sex: 'M',
  14. name: "zled",
  15. }
  16. fmt.Println(p2)
  17. //new方式,未设置初始值的,会赋予类型的默认初始值
  18. p3 := new(User)
  19. p3.name = "zzled"
  20. p3.sex = 'M'
  21. fmt.Println(p3)
  22. // 普通定义,分别设置
  23. var p4 User
  24. p4.sex = 'F'
  25. p4.age = 18
  26. p4.name = "ada"
  27. fmt.Println(p4)
  28. }
  29. /*
  30. {ada 19 70}
  31. {zled 24 77}
  32. &{zzled 0 77}
  33. {ada 18 70}
  34. */

1.3 结构体的访问

直接通过对象名.field进行访问即可

  1. p1 := User{"ada", 19, 'F'}
  2. fmt.Println(p1.name)
  3. /*
  4. ada
  5. */

1.4 结构体指针

用法与普通指针相同,可以查看上一节的函数指针使用。

  1. type User struct {
  2. name string
  3. age int
  4. sex byte
  5. }
  6. func main() {
  7. u := User{"ada", 19, 'F'}
  8. fmt.Println(u)
  9. structPointer(&u)
  10. }
  11. /*结构体指针*/
  12. func structPointer(u *User) {
  13. fmt.Println("指针所指变量的地址是: ", u)
  14. fmt.Println("指针所指变量的值是: ", *u)
  15. fmt.Printf("%T\n", u)
  16. fmt.Printf("%s\n", u.name)
  17. fmt.Printf("%d\n", u.age)
  18. fmt.Printf("%c\n", u.sex)
  19. }
  20. /*
  21. {ada 19 70}
  22. 指针所指变量的地址是: &{ada 19 70}
  23. 指针所指变量的值是: {ada 19 70}
  24. *main.User
  25. ada
  26. 19
  27. F
  28. */

1.5 匿名字段(继承字段)

可以用字段来创建结构,这些字段只包含一个没有字段名的类型。这些字段被称为匿名字段。

  1. type Human struct {
  2. name string
  3. age int
  4. weight int
  5. }
  6. type Student struct {
  7. Human // 匿名字段,那么默认Student就包含了Human的所有字段
  8. speciality string
  9. }
  10. func main() {
  11. s := Student{
  12. Human: Human{"ada", 19, 45},
  13. speciality: "resident Evil",
  14. }
  15. fmt.Println(s)
  16. fmt.Println(s.name)
  17. fmt.Println(s.age)
  18. fmt.Println(s.weight)
  19. fmt.Println(s.speciality)
  20. }
  21. /*
  22. {{ada 19 45} resident Evil}
  23. ada
  24. 19
  25. 45
  26. resident Evil
  27. */

可以使用”.”的方式进行调用匿名字段中的属性值 实际就是字段的继承

1.6 结构体嵌套

一个结构体可能包含一个字段,而这个字段反过来就是一个结构体。这些结构被称为嵌套结构。
示例:

  1. type Address struct {
  2. city, state string
  3. }
  4. type Person struct {
  5. name string
  6. age int
  7. address Address
  8. }
  9. func main() {
  10. var p Person
  11. p.name = "ada"
  12. p.age = 21
  13. p.address = Address{
  14. city: "Chicago",
  15. state: "Illinois",
  16. }
  17. fmt.Println("Name:", p.name)
  18. fmt.Println("Age:", p.age)
  19. fmt.Println("City:", p.address.city)
  20. fmt.Println("State:", p.address.state)
  21. }
  22. /*
  23. Name: ada
  24. Age: 21
  25. City: Chicago
  26. State: Illinois
  27. */

1.7 提升字段

在结构体中属于匿名结构体的字段被称为提升字段,因为它们可以被访问,就好像他们属于拥有匿名结构字段的结构一样。理解这个定义是相当复杂的。

  1. type Address struct {
  2. city, state string
  3. }
  4. type Person struct {
  5. name string
  6. age int
  7. Address // 注意与1.6例子的区别
  8. }
  9. func main() {
  10. var p Person
  11. p.name = "ada"
  12. p.age = 21
  13. p.Address = Address{ // 注意与1.6例子的区别
  14. city: "Chicago",
  15. state: "Illinois",
  16. }
  17. fmt.Println("Name:", p.name)
  18. fmt.Println("Age:", p.age)
  19. fmt.Println("City:", p.city) // 注意与1.6例子的区别
  20. fmt.Println("State:", p.state) // 注意与1.6例子的区别
  21. }
  22. /*
  23. Name: ada
  24. Age: 21
  25. City: Chicago
  26. State: Illinois
  27. */

1.9 结构体比较

结构体是值类型,如果每个字段都具有可比性,则是可比较的,如果他们对应的字段相等,则认为两个结构体变量是相等的。
示例:

  1. type Person struct {
  2. name string
  3. age int
  4. }
  5. func main() {
  6. var p1 Person
  7. p1.name = "ada"
  8. p1.age = 21
  9. var p2 Person
  10. p2.name = "ada"
  11. p2.age = 21
  12. var p3 Person
  13. p3.name = "lion"
  14. p3.age = 20
  15. fmt.Println(p1 == p2)
  16. fmt.Println(p2 == p3)
  17. fmt.Println(p1 == p3)
  18. }
  19. /*
  20. true
  21. false
  22. false
  23. */

1.8 结构体作为函数参数

当成普通的参数使用即可。

1.9 make()和new()的区别

看起来没有区别,都是在堆上分配内存,但它们的行为不同,适用于不同的类型。

  • new(T) 为每个新的类型T分配一片内存,初始化为0并且返回类型为*T的内存地址:这种方法返回一个指向类型为T,值为0的地址的地址,它适用于值类型如:数组和结构体;它相当于&T{}。
  • make(T) 返回一个类型为T的初始值,它只适用于3种内建的引用类型:slice、map和channel。

换言之,new函数分配内存,make函数初始化。
image.png
第一幅图中:

  1. var p *[]int = new([]int) // *p == nil; with len and cap 0
  2. p := new([]int)

第二幅图中:

  1. p :=make([]int,0)

切片已经被初始化,但是指向一个空的数组。
以上两种方式实用性都不高。下面的方法比较实用:

  1. var a []int = make([]int,10,50)
  2. b := make([]int,10,50)

这样分配一个有50个int值的数组,并且创建一个长度为10,容量为50的切片v,该切片指向数组的前10个元素。

  1. // 如果只有一个参数,那么长度和容量相同
  2. v := make([]int, 10)
  3. fmt.Printf("长度为%d,容量为%d\n", len(v), cap(v))
  4. // 如果数字有两个,则第一个是长度,第二个是容量,且必须长度小于等于容量
  5. b := make([]int, 10, 50)
  6. fmt.Printf("长度为%d,容量为%d\n", len(b), cap(b))
  7. /*
  8. 长度为10,容量为10
  9. 长度为10,容量为50
  10. */

初始完成后,会动态进行调整。

  1. v := make([]int, 3, 5)
  2. fmt.Printf("长度为%d,容量为%d\n", len(v), cap(v))
  3. b := [4]int{1, 2, 3, 4}
  4. v = b[:]
  5. fmt.Println(v)
  6. fmt.Printf("长度为%d,容量为%d\n", len(v), cap(v))
  7. d := [6]int{1, 2, 3, 4, 5, 6}
  8. v = d[:]
  9. fmt.Printf("长度为%d,容量为%d\n", len(v), cap(v))
  10. /*
  11. 长度为3,容量为5
  12. [1 2 3 4]
  13. 长度为4,容量为4
  14. 长度为6,容量为6
  15. */

获取长度和容量

  1. v := make([]int, 3, 5)
  2. fmt.Printf("长度为%d,容量为%d\n", len(v), cap(v))
  3. d := [5]int{1, 2, 3, 4, 5}
  4. v = d[1:4]
  5. fmt.Println(v)
  6. fmt.Printf("长度为%d,容量为%d\n", len(v), cap(v))
  7. /*
  8. 长度为3,容量为5
  9. [2 3 4]
  10. 长度为3,容量为4
  11. */