终于迎接来 Go 语言最最核心的数据类型 —— struct. 没错,这和 C 语言里的结构体几乎一模一样。不过 Go 里的结构体,会比 C 语言的结构多一些东西,这些后面再讲。
本文先简单认识一下 Go 的结构体是什么样子,以及怎么声明和定义。
1. 声明与定义
- 例1
package main
import "fmt"
func main() {
// 定义了一个变量 p,它是一个结构体对象。
var p struct {
id int
name string
age int
height int
}
p.name = "allen"
p.age = 20
p.height = 180
fmt.Println(p) // Output: {0 allen 20 180}
}
上面的代码定义了一个变量 p,类型是一个结构体,跟在 p 的后面。
如果另外一个地方也用到了和 p 同样类型的的结构体怎么办呢?没错,再写一遍就好啦^_^。开个玩笑,程序员是一种很懒的生物,肯定不愿意这么干。不知道你是否记得 type 关键字?如果忘记了,请看这一篇 《type 关键字》。
要想复用一种结构体类型,可以这样写:
- 例2
package main
import "fmt"
func main() {
type person struct {
id int
name string
age int
height int
}
// var p person // 这种声明 ok
// p := person{} // 这种也 ok
// p := person{5, "allen", 20, 180} // 这种使用字面量也是 ok 的
p := person{id: 5} // 这种指定成员名称初始化值,也 ok
p.name = "allen"
p.age = 20
p.height = 180
fmt.Println(p) // Output: {5 allen 20 180}
}
从例 2 中可以看到,使用 type 关键字给结构体取了个名字叫 person,后面我们就可以全部使用 person 这种新的类型来定义变量了。上面也列举了 4 种不同的定义变量的方式:
- 使用 var 声明,这种是最传统的办法了
- 使用 {} 声明一个空对象
- 使用『字面量』初始化一个对象
- 指定成员初始化值
有同学会问,这样行不行:
p := person{5, "allen"}
答:不行。使用字面量初始化,就必须给所有成员指定一个值。如果你只想给部分成员指定值,那就必须要把成员名字加上:
p := person{id:5, name: "allen"}
2. 访问 struct 成员
2.1 使用普通变量访问成员
在 Go 里访问 struct 成员像 C 语言一样,使用 .
号。例如:
fmt.Printf("id = %d\n", p.id) // 读取成员的值
p.id = 10 // 给成员赋值
2.2 使用指针访问成员
如果我们通过指针来访问结构体成员,应该是什么样的呢?
type person struct {
id int
name string
age int
height int
}
p := person{5, "allen", 20, 180}
pp := &p
fmt.Println(pp) // Output: &{5 allen 20 180}
fmt.Println(*pp) // Output: {5 allen 20 180}
通过 pp 访问成员应该怎么办?下面是一种办法:
(*pp).name = "zoro"
不过,上面这种写法太繁琐,Go 里可以这样写:
pp.name = "zoro" // 是不是非常具有迷惑性?为什么不是 pp->name = "zoro" 这样多直观 TT
所以使用 .
语法,真的看不出来 .
前面的变量到底是普通变量,还是指针变量的指针。
使用指针的好处是,我们可以通过指针间接修改对象的值。比如可以让函数参数作为指针结构体变量的指针传进去,这样就可以在函数内部通过指针修改成员的值,从而改变函数外的结构体对象了。
2.3 指向结构体成员的指针
不仅可以取结构体变量的地址,还可以取结构体成员的地址:
type person struct {
id int
name string
age int
height int
}
p := person{5, "allen", 20, 180}
pname := &p.name
*pname = "zoro" // 通过指针直接修改成员的值
fmt.Println(p) // Output: {5 zoro 20 180}
3. 总结
- 掌握结构体声明与定义
- 掌握通过结构体指针访问成员的方法