概述
结构体是一种聚合的自定义数据类型,是由零个或任意多个相同或不同类型的变量聚合而成的数据集合,每个变量称为结构体成员(也可称之为成员变量,字段,属性)。
定义结构体
使用 type 和 struct 语句定义结构体,type 语句设定了结构体的类型名称,struct 语句定义一个新的数据类型,结构体中有一个或多个成员。
格式如下:
/*
type 类型名 struct {
字段1 字段1类型
字段2 字段2类型
…
字段3 字段3类型
}
*/
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
// 或者 同一类型的字段,可以定义在一行
type Color struct {
R, G, B byte
}
注意:
- 如果一个结构体的成员变量名称是首字母大些的,那么这个变量是可导出的(即在其它包可以访问到),反之不可导出。如下:
type A struct {
Hour int // 可导出
minute int // 不可导出
}
- 结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存。
结构体实例化
- 通过 var 声明结构体完成实例化
结构体本身是一种类型,可以像整型、字符串等类型一样,以 var 的方式声明结构体即可完成实例化。
structure_variable_type 为结构体类型,variable_name 为结构体的实例。
// 给 variable_name 分配内存,并零值化内存,但是这个时候的 variable_name 的类型是 structure_variable_type
var variable_name structure_variable_type
- 使用 new() 实例化
使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:var variable_name *structure_variable_type = new(structure_variable_type)。
variable_name := new(structure_variable_type)
- 使用字面量初始化
variable_name := structure_variable_type {value1, value2, valuen3}
或
variable_name := structure_variable_type {key1: value1, key2: value2, key3: value3}
variable_name := &structure_variable_type {value1, value2, valuen3} // 等效于 new(structure_variable_type)
使用结构体
1. 访问结构体成员
要访问结构体成员,需要使用点号 . 操作符,格式为:
// 结构体.成员名
type Point struct {
X int
Y int
}
var p Point
p.X = 10
p.Y = 20
2. 比较结构体
如果结构体的所有成员变量都可以比较,那么这个结构体是可以比较的。两个结构体的比较可以使用 == 或者 !=。
例如:
type C struct {
A int
B string
}
c1 := C{A:1, B:"abc"}
c2 := C{A:1, B:"abc"}
c3 := C{A:2, B:"abc"}
fmt.Println(c1.A == c2.A && c1.B == c2.B) // true
fmt.Println(c1 == c2) // true 与上等价
fmt.Println(c1.A == c3.A && c1.B == c3.B) // false
fmt.Println(c1 == c3) // false 与上等价
和其他可比较的类型一样,可比较的结构体类型都可以作为map的键类型。
例如:
type C struct {
A int
B string
}
mp := make(map[C]int)
key := C{A:1, B:"abc"}
mp[key] = 9
fmt.Println(mp[C{A:1, B:"abc"}]) // 9
3. 结构体嵌套和匿名成员
Go语言允许我们定义不带(省略)字段名只写类型的结构体成员,这种结构体成员称作匿名成员。因为结构体中字段名必须是唯一的,所以匿名成员的类型也必须是唯一的;
结构体成员的类型必须是一个命名类型或者指向命名类型的指针。正是因为有了这种结构体嵌套的功能,我们才能直接访问到我们需要的变量而不是指定一大串中间变量。
type Point struct {
X int
Y int
}
type Circle struct {
Point
}
var c Circle
c.X = 10 // 等价于 c.Point.X = 10
c.Y = 10 // 等价于 c.Point.Y = 10
type Wheel struct {
*Point
}
结构体字面量初始化没有快捷方式,必须遵循形状类型的定义。
type Point struct {
X int
Y int
}
type Circle struct {
Point
}
var c Circle
c = Circle{1,1} // 错误
c = Circle{Point{1,1}} // 正确
c = Circle{Point: Point{1,1}} // 正确
因为“匿名成员”拥有隐式的名字,所以你不能在一个结构体里面定义两个相同类型的匿名成员,否则会引起冲突。由于匿名成员的名字是由它们的类型决定的,因此它们的可导出性也是由他们的类型决定。在下面的例子中,point和circle这两个匿名成员是可导出的,即使这两个结构体是不可导出的(point和circle)。
type point struct {
X int
Y int
}
type circle struct {
point
}
type Wheel struct {
circle
}
var w Wheel
w.X = 8 // 等价于 w.circle.point.X = 8, w.X是可导出的,w.circle.point.X是不可导出的
4. 结构体作为函数参数
可以像其他数据类型一样将结构体类型作为参数传递给函数。并以实例的方式访问结构体变量。 例如:
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func changeBook(book Books) {
book.title = "book1_change"
fmt.Println(book)
}
func main() {
var book1 Books;
book1.title = "book1"
book1.author = "zuozhe"
book1.book_id = 1
changeBook(book1)
fmt.Println(book1)
}
函数中结构体作为参数,如果不是用结构指针,函数内参数属性的改变不影响原来结构体数据。 结果为:
{book1_change zuozhe 1}
{book1 zuozhe 1}
5. 结构体指针
可以定义指向结构体的指针类似于其他指针变量,格式如下:
var struct_pointer *Books
以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前:
struct_pointer = &Book1
使用结构体指针访问结构体成员,使用 "." 操作符:
struct_pointer.title