十四、结构体
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。比如你需要定义一些互相有联系的变量,而变量之间的类型不同。
举个实际的例子,保存图书馆的书籍记录,每本书有以下属性:title
,author
,price
,此时就可以使用结构体类型。
1 定义结构体
结构体定义需要使用type
和struct
语句。struct
语句定义一个新的数据类型,结构体中有一个或多个成员。type
语句设定了结构体的名称。
type struct_variable_type struct {
member definition
member definition
...
member definition
}
// struct_variable_type为自定义的结构体类型名称
// 在结构体内部定义成员(member definition)
一旦定义了结构体,就可以将它作为变量的声明
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
示例:
type Books struct {
title string
author string
price int
}
func main() {
var book Books // 声明变量为Books结构体类型
}
2 访问结构体成员
果要访问结构体成员,需要使用点.
操作符。
type Books struct {
title string
author string
price int
}
func main() {
var book Books // 声明但未初始化赋值
fmt.Println(book.price) // 未赋值时,结构体为零值
}
3 结构体成员赋值
可以在声明时赋值;也可以先声明,再使用.
操作符赋值。
type Books struct {
title string
author string
price int
}
func main() {
var book = Books{
title: "西游记",
author: "吴承恩",
price: 60,
}
// 也可以如下方式赋值
//book.title = "西游记"
//book.author = "吴承恩"
//book.price = 60
fmt.Println(book)
}
你可以为结构体的部分成员赋值,此时剩下的成员会被赋值为零值。
4 结构体的导出
定义结构体及其成员时,仍遵循大写导出,小写不导出的规则。如下:
type Books struct { // 大写结构体名字,可以导出,外部包可以访问
title string // 小写成员名字,不能被导出,外部包无法访问
author string // 小写成员名字,不能被导出,外部包无法访问
Price int // 大写成员名字,可以导出,外部包可以访问
}
如果想要让这个结构体所有成员被外部访问,需要将它们大写。
5 结构体的函数传递
你可以像其他数据类型一样将结构体类型作为参数传递给函数,并且可以访问内部成员。
type Books struct {
Title string
Author string
Price int
}
func change(book Books) {
book.Title = "红楼梦"
book.Author = "曹雪芹"
book.Price = 50
}
func main() {
var book = Books{
Title: "西游记",
Author: "吴承恩",
Price: 60,
}
fmt.Println(book)
change(book) // 结构体按值传递,在函数内部的更改不影响原来的值
fmt.Println(book)
}
在函数内修改结构体,原来的结构体不会改变。
6 结构体指针与结构体的函数引用传递
要想在函数内更改结构体,可以利用结构体指针。相当于结构体的引用传递。
type Books struct {
Title string
Author string
Price int
}
func change(book *Books) {
book.Title = "红楼梦"
book.Author = "曹雪芹"
book.Price = 50
}
func main() {
var book = Books{
Title: "西游记",
Author: "吴承恩",
Price: 60,
}
var ptr *Books = &book // 定义结构体指针,指向book
fmt.Println(book)
change(ptr) // 传递指针,通过指针修改原结构体
fmt.Println(book) // 此时是引用传递,函数内的更改在外部生效
}
7 匿名结构体
我们之前定义的结构体称为命名结构体,因为我们创建了新的类型;匿名结构体是是一个变量,不创建新的类型。
func main() {
// 匿名结构体
var Book struct {
title string
author string
price int
}
// 结构体赋值
Book.title = "西游记"
Book.author = "吴承恩"
Book.price = 50
fmt.Println(Book)
}
也可以在声明同时赋值
func main() {
// 创建匿名结构体,使用变量Book接收它,并为它的成员赋值
Book := struct {
title string
author string
price int
}{
title: "西游记",
author: "吴承恩",
price: 50,
}
fmt.Println(Book)
}
8 结构体嵌套
结构体内部还可以定义结构体。示例:
type Book struct {
Title string
Author struct {
Name string
Age int
}
price int
}
func main() {
book := Book{
Title: "西游记",
Author: struct {
Name string
Age int
}{
Name: "吴承恩",
Age: 18,
},
price: 60,
}
fmt.Println(book)
}
9 匿名字段
定义结构体时,成员字段可以只有类型而没有字段名,这种形式的结构体字段为匿名字段。
// 定义匿名字段
type Book struct {
string
int
}
func main() {
book := Book{
string: "红楼梦",
int: 60,
}
// 如果字段没有名称,那么默认它的名称为它的类型
fmt.Println(book)
}
如果字段没有名称,那么默认它的名称为它的类型。比如string
字段和int
字段。
10 结构体比较
结构体是值类型(通过函数传递也可以看出来),如果它的所有字段都可比较,则该结构体也是可比较的;如果结构体包含不可比较的字段,则结构体也不可比较。