什么是结构体
- 结构体是由一系列具有相同或不同数据类型的字段组合而成的复合类型
- type movie struct {
name string
rating float32
} - movie是一种结构体类型,包括name和rating两个字段,其类型分别为string和float32
- type movie struct {
- 通过结构体可在单个变量中保存多个类型相同或不同的数据字段,即封装性。
- 结构体是一种自定义的复杂数据类型,可以弥补内置类型表现力不佳的缺陷。
- 结构体类型的变量携其各个字段作为一个整体被赋值、传参和从函数中返回。
- 结构体还可作为JSON字符串、数据库记录、通信报文等数据体的描述模板。
- 无论是结构体变量还是其指针都可以通过"."加字段名的形式访问其各个字段。
// 结构体由一系列具有指定数据类型的字段组合而成
//
// type 结构体 struct {
// 字段1 类型1
// 字段2 类型2
// ...
// 字段n 类型n
// }
package main
import "fmt"
type movie struct {
name string
rating float32
}
func main() {
m := movie{ // 简短方式声明方式
name: "Citizen Kane",
rating: 10,
}
fmt.Println(m.name, m.rating) // Citizen Kane 10
fmt.Println(m) // {Citizen Kane 10}
fmt.Printf("%v\n", m) // %v可以输出结构体中每个字段的值
// {Citizen Kane 10}
fmt.Printf("%+v\n", m) // %+v可以输出结构体中每个字段的字段名与字段值
// {name:Citizen Kane rating:10}
fmt.Printf("%#v\n", m) // %#v可以输出结构体中每个字段的字段名与字段值,
// 并显示类型信息
// main.movie{name:"Citizen Kane", rating:10}
}
如何创建结构体
结构体的创建方式,常见的有5种:
- 创建结构体类型的变量与创建其它类型的变量并没有本质性的差别,使用之前定义的结构体类型名即可
- var m movie // 结构体中的每个字段默认初始化为适当类型的零值
- var m = movie{"Citizen Kane", 10} // 按字段在结构体中被声明的顺序设置初值
- var m = movie{rating: 10, name: "Citizen Kane"} // 按字段名初始化,顺序无所谓
- m := movie{
rating: 10, // 每个字段独占一行,提高可读性
name: "Citizen Kane", // 最后一行必须以逗号结尾
}
// 声明结构体变量时,如果没有指定初值,其各个字段均被设置
// 为相应类型的零值,此后可通过“.”号为其各个字段赋予初值
// 变量.字段1 = 初值1
// 变量.字段2 = 初值2
// ...
// 变量.字段n = 初值n
package main
import "fmt"
type movie struct {
name string
rating float32
}
func main() {
var m movie
fmt.Printf("%+v\n", m) // {name: rating:0}
m.name = "Citizen Kane"
m.rating = 10
fmt.Printf("%+v\n", m) //{name:Citizen Kane rating:10}
}
用简短的方式实例化结构体:
// 变量 := 结构体{初值, ...}
// 变量 := 结构体{字段: 初值, ...}
// 变量 := 结构体{
// 字段: 初值,
// ...,
// }
package main
import "fmt"
type movie struct {
name string
rating float32
}
func main() {
m := movie{
name: "Citizen Kane",
rating: 10,
}
fmt.Printf("%+v\n", m) //{name:Citizen Kane rating:10}
}
- 关键字new可用于创建包括结构体在内的任何类型的变量,所得到的是指向该变量的指针。
- var m = new(movie)
- m := new(movie)
在Go语言中,编译器自动选择在栈上还是堆上为变量分配内存,这与变量的生命期有关,而与是否使用关键字new无关。
- func foo() *int {
loc := 5 // 在堆上分配内存,虽然是个局部变量(理论上应该在栈内存区),但因为其地址被返回(函数外部可能会访问loc变量),因此编译器根据上下文将在分配在了堆内存区。
return &loc // 逃逸的局部变量
} - func bar() {
ptr := new(int) // 在栈上分配内存,new创建理论上应该在堆内存区。
*ptr = 5 // *ptr没有逃逸
}
使用fmt包的格式化函数打印结构体变量
- %v只打印各字段的值。
- %+v除了打印字段值还会打印字段名。
- %#v除打印字段名和字段值以外,还会将结构体名和包名一并打印出来。
- fmt.Printf("%v, %+[1]v, %#[1]v\n", m)
- {Citizen Kane 10}, {name:Citizen Kane rating:10}, main.movie{name:"Citizen Kane", rating:10}
- 动词前面的"[i]",表示使用格式化字符串后面的第i个操作数
Go语言对结构体的支持,并非意图引入面向对象的程序设计理念,而仅仅是将其作为一种数据结构的表现形式,以满足数据建模的一般需求。
// 通过new实例化结构体变量
// 使用关键字new创建结构体实例,获得其地址
// var 指针 = new(结构体)
// 指针 := new(结构体)
package main
import "fmt"
type movie struct {
name string
rating float32
}
func main() {
var m = new(movie)
fmt.Printf("%+v\n", m) // &{name: rating:0},因为new创建的结构体返回
// 的是指向该结构体的指针,所以,在打印时出现了
// &符号,与我们之前看到的输出有所不同。
m.name = "Citizen Kane"
m.rating = 10
fmt.Printf("%+v\n", m) // &{name:Citizen Kane rating:10}
}