跳到主要内容

结构体的实例化

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

实例化方式包括如下几种。

2.1、声明结构体变量再赋值

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

package main

import "fmt"

type Student struct {
sid int
name string
age int8
course []string // 选秀课程
}

func main() {

// 声明一个结构体对象 ,值类型,默认开辟空间,字段赋予零值
var s Student
fmt.Println("s:", s)
// 要访问结构体成员,需要使用点号 . 操作符
fmt.Println(s.name)
// 更改成员变量的值
s.name = "yuan"
fmt.Println(s.name)
// s.course[0] = "chinese" // 结果,如何调整
}
提示

1、结构体属于值类型,即var声明后会像整形字符串一样创建内存空间。

提示

2、创建结构体对象如果没有给字段赋值,则默认零值(字符串默认 "",数值默认0,布尔默认false,切片和map默认nil对象)

结构体的内存存储:

package main

import "fmt"

type Student struct {
sid int
name string
age int8
course []string // 选秀课程
}

func main() {
// 声明一个结构体对象 ,值类型,默认开辟空间,字段赋予零值
var s Student
fmt.Println("s:", s)
s.sid = 1001
s.name = "yuan"
s.age = 23
s.course = []string{"chinese", "math", "english"}
fmt.Printf("%p\n", &s)
fmt.Printf("%p\n", &(s.sid))
fmt.Printf("%p\n", &(s.name))
fmt.Printf("%p\n", &(s.age))
fmt.Printf("%p\n", &(s.course)) // 切片24个字节
}

截屏2022-09-03 22.12.24

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

2.2、实例化之 结构体

// (1) 方式1
s1 := Student{}
s1.sid = 1001
s1.name = "yuan"

// (2) 方式2:键值对赋值
s2 := Student{sid: 1002, name: "rain", course: []string{"chinese", "math", "english"}}
fmt.Println(s2)

// (3) 方式3:多值赋值
s3 := Student{1003, "alvin", 22, []string{"chinese", "math", "english"}}
fmt.Println(s3)
提示

1、结构体可以使用“键值对”(Key value pair)初始化字段,每个“键”(Key)对应结构体中的一个字段,键的“值”(Value)对应字段需要初始化的值。键值对的填充是可选的,不需要初始化的字段可以不填入初始化列表中,走默认值。

提示

2、多值初始化方式必须初始化结构体的所有字段且每一个初始值的填充顺序必须与字段在结构体中的声明顺序一致。

提示

3、键值对与值列表的初始化形式不能混用。

2.3、实例化之&结构体

package main

import "fmt"

type Student struct {
sid int
name string
age int8
course []string // 选秀课程
}

func CourseInit(stu Student) {
stu.course = []string{"chinese", "math", "english"}
fmt.Println(stu)
}

func CourseInit2(stu *Student) {
(*stu).course = []string{"chinese", "math", "english"}
}

func main() {
// 案例1
s1 := Student{sid: 1001, name: "alvin", age: 32}
s2 := s1 // 值拷贝
fmt.Println(s2)
s1.age = 100
fmt.Println(s2.name)

// 如果希望s3的值跟随s2保持一致怎么实现
s3 := &s1 // var s4 *Student = &s2
s1.age = 100
fmt.Println((*s3).age)
fmt.Println(s3.age)

// 案例2
var s4 = Student{sid: 1001, name: "alvin", age: 32}
CourseInit(s4)
fmt.Println("s报的课程:", s4.course)
// 怎么能初始化成功呢?
var s5 = &Student{sid: 1001, name: "alvin", age: 32}
CourseInit2(s5)
fmt.Println("s报的课程:", (*s5).course) // *s.course的写法是错误的
fmt.Println("s报的课程:", s5.course)
}

提示

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

9.2.4、实例化之 new(结构体)

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

instance := new(T)

其中:

  • T 为类型,可以是结构体、整型、字符串等。

  • instance:T 类型被实例化后保存到 instance 变量中,instance的类型为 *T,属于指针。

s := new(Student)              // &Student{}
fmt.Println(reflect.TypeOf(s)) // *Student
fmt.Println(s) // *Student

s.name = "yuan"
fmt.Println((*s).name)
fmt.Println(s.name)