结构体的定义只是一种内存布局的描述,只有当结构体实现实例化时,才会真正地分配内存,因此必须在定义结构体并实例化后才能使用结构体的字段。实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的内存是完全独立的。
实例化方式包括如下几种。

1、实例化之 :var 变量名 结构体

结构体本身是一种类型,它属于值类型,可以像整型、字符串等类型一样,以var的方式声明结构体即可完成实例化。

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. sid, age int
  5. name string
  6. sex byte
  7. score [3]int
  8. }
  9. func main() {
  10. // 声明一个结构体对象,值类型,默认开辟空间,字段默认赋予零值
  11. var s Student
  12. fmt.Println(s) //{0 0 0 [0 0 0]}
  13. // 要访问结构体成员,需要使用点号 . 操作符
  14. fmt.Println(s.name) // ""
  15. // 更改成员变量
  16. s.name = "sgg"
  17. s.score[0] = 100
  18. fmt.Println(s.name) // "sgg"
  19. fmt.Println(s.score) // [100 0 0]
  20. }
  1. 结构体属于值类型,即var 声明后会像整型、字符串一样创建内存空间
  2. 创建结构体对象,如果没有给字段赋值,则默认零值(字符串默认 “”,数值默认0,布尔默认false,切片和map默认nil对象)

结构体的内存存储:

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. name string
  5. age int
  6. }
  7. func main() {
  8. var s1 Student
  9. fmt.Println(s1) // { 0}
  10. var s2 Student
  11. s2.name = "sgg"
  12. s2.age = 22
  13. fmt.Printf("%p\n", &s2) // 0xc00009a018
  14. fmt.Printf("%p\n", &(s2.name)) // 0xc00009a018
  15. fmt.Printf("%p\n", &(s2.age)) // 0xc00009a028
  16. }

8.2、结构体实例化 - 图1
之前我们学习过值类型和引用类型,知道值类型是变量对应的地址直接存储值,而引用类型是变量对应地址存储的是地址。因为结构体是值类型,所以对象s的地址与存储的第一个值的地址相同的,而后面每一个成员变量的地址是连续的。

2、实例化之 :结构体{}

  1. type Student struct {
  2. name string
  3. age int
  4. }
  5. // 方式1
  6. var s1 = Student{}
  7. s1.name = "sgg"
  8. // 方式2:键值赋值
  9. var s2 = Student{name: "sgg", age: 22}
  10. fmt.Println(s2.name) //sgg
  11. fmt.Println(s2.age) //22
  12. // 方式3:多值赋值
  13. var s3 = Student{"sgg",22}
  14. fmt.Println(s3.name) //sgg
  15. fmt.Println(s3.age) //22
  1. 结构体可以使用 “键值对” (key value pair) 初始化字段,每个”键”(Key)对应结构体中的一个字段,键的”值”(value)对应字段需要初始化的值。键值对的填充是可选的,不需要初始化的字段可以不填入初始化列表中。
  2. 多值初始化必须初始化结构体的所有字段且每一个初始值的填充顺序必须与字段在结构体中的声明顺序一致。
  3. 键值对与值列表的初始化形式不能混用。

3、实例化之 :&结构体{}

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. name string
  5. age int
  6. }
  7. func changeAge(stu *Student) {
  8. stu.age = 23
  9. }
  10. func main() {
  11. s1 := Student{name: "alvin", age: 22}
  12. s2 := s1 // 值copy,s2 跟 s1 不属于内存地址
  13. s1.name = "sgg"
  14. fmt.Println(s2.name) // alvin
  15. fmt.Println(s1.name) // sgg
  16. // 如果希望s3的值跟随s2保持一致怎么实现, 就需要用到地址
  17. s3 := &s1 // var s3 *Student = &s2
  18. fmt.Println(s3.name)
  19. // 取地址
  20. s := &Student{name: "sbh", age: 33}
  21. changeAge(s)
  22. fmt.Println((*s).age) // 23,*s.course的写法是错误的
  23. fmt.Println(s.age) // 23 => Go语言这边做了 语法糖 把 (*s).age 形式转成了s.age
  24. }

在Go语言中,访问结构体指针的成员变量时,可以继续使用 . ,这是因为Go语言为了方便开发者访问结构体指针的成员变量可以像访问结构体的成员变量一样简单,使用了 语法糖(Syntactic sugar) 技术,将 instance.name 形式转换为 (*instance).name
8.2、结构体实例化 - 图2

4、实例化之: New(结构体)

Go语言中,还可以使用 new 关键字对类型(包括结构体、整型、浮点型、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。使用new的格式如下:

  1. instance := new(T)

其中:

  • T 为类型,可以是结构体、整型、字符串等。
  • instance: T 类型被实例化后保存到 instance 变量中,instance 的类型为 *T,属于指针。
  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Student struct {
  7. name string
  8. age int
  9. }
  10. func main() {
  11. s := new(Student) // &Student{}
  12. fmt.Println(reflect.TypeOf(s)) // *Student
  13. s.name = "sgg"
  14. fmt.Println(s.name) // sgg
  15. }

存放多个结构体对象:

  1. package main
  2. import "fmt"
  3. type Student struct {
  4. name string
  5. age int
  6. sex byte
  7. addr string
  8. }
  9. func main() {
  10. s1 := new(Student)
  11. s2 := new(Student)
  12. s3 := new(Student)
  13. s4 := new(Student)
  14. s := []*Student{s1, s2, s3, s4} // 存储多个指针类型的结构体对象
  15. fmt.Println(s)
  16. }