#《Go 语音圣经(中文版)》

#复合数据类型

#结构体

结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体,每个值称为结构体的成员。

结构体变量的成员可以通过点操作符访问,或者是对成员取地址,然后通过指针访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// -------------------------------------------
// Created By : Onns onns@onns.xyz
// File Name : 4.5.go
// Purpose :
// Creation Date : 2021-04-19 12:14:48
// Last Modified : 2021-04-19 12:18:52
// -------------------------------------------

package main

import (
"fmt"
"time"
)

type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}

func main() {
var dilbert Employee
dilbert.Salary -= 5000
position := &dilbert.Position
*position = "Senior " + *position

fmt.Println(dilbert)
}
1
2
[12:18:52] onns@Onns ~/Documents/code/go/go-bible/ch4 $ go run 4.5.go
{0 0001-01-01 00:00:00 +0000 UTC Senior -5000 0}

点操作符也可以和指向结构体的指针一起工作:

1
2
3
4
5
var employeeOfTheMonth *Employee = &dilbert
employeeOfTheMonth.Position += " (proactive team player)"

// 相当于下面语句
(*employeeOfTheMonth).Position += " (proactive team player)"

通常一行对应一个结构体成员,成员的名字在前类型在后,如果相邻的成员类型如果相同的话可以被合并到一行:

1
2
3
4
5
6
7
8
type Employee struct {
ID int
Name, Address string
DoB time.Time
Position string
Salary int
ManagerID int
}

如果结构体成员名字是以大写字母开头的,那么该成员就是导出的。一个结构体可能同时包含导出和未导出的成员。[1]

一个命名为S的结构体类型将不能再包含S类型的成员:因为一个聚合的值不能包含它自身,但是S类型的结构体可以包含*S指针类型的成员,这可以让我们创建递归的数据结构,比如链表和树结构等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// -------------------------------------------
// Created By : Onns onns@onns.xyz
// File Name : 4.6.go
// Purpose :
// Creation Date : 2021-04-19 12:14:48
// Last Modified : 2021-04-20 16:53:48
// -------------------------------------------

package main

import (
"fmt"
"time"
)

type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}

func main() {
var d Employee
d.Salary -= 5000
position := &d.Position
*position = "Senior " + *position

fmt.Println(d)

e := &d
fmt.Printf("%T %T\n", e, d)
e.Position += " (proactive team player)"
fmt.Println(d)

d.Position += " ?"
fmt.Println(d)
// 为什么这里 &d 和 d 是等效的?
}

发现一个问题:为什么这里&dd是等效的?我很好奇。。。

1
2
3
4
5
[16:53:50] onns@Onns ~/Documents/code/go/go-bible/ch4 $ go run 4.6.go
{0 0001-01-01 00:00:00 +0000 UTC Senior -5000 0}
*main.Employee main.Employee
{0 0001-01-01 00:00:00 +0000 UTC Senior (proactive team player) -5000 0}
{0 0001-01-01 00:00:00 +0000 UTC Senior (proactive team player) ? -5000 0}
1
2
3
4
5
6
func EmployeeByID(id int) *Employee { /* ... */ }

fmt.Println(EmployeeByID(dilbert.ManagerID).Position) // "Pointy-haired boss"

id := dilbert.ID
EmployeeByID(id).Salary = 0 // fired for... no real reason

后面的语句通过EmployeeByID返回的结构体指针更新了Employee结构体的成员。如果将EmployeeByID函数的返回值从*Employee指针类型改为Employee值类型,那么更新语句将不能编译通过,因为在赋值语句的左边并不确定是一个变量(译注:调用函数返回的是值,并不是一个可取地址的变量)。

上面这句话,我感觉也没理解,自己测试了一下发现可以通过编译?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// -------------------------------------------
// Created By : Onns onns@onns.xyz
// File Name : 4.7.go
// Purpose :
// Creation Date : 2021-04-19 12:14:48
// Last Modified : 2021-04-20 17:13:00
// -------------------------------------------

package main

import (
"fmt"
"time"
)

type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}

var d Employee
var e *Employee

// func init() {
// d.Salary = 5000
// e.Salary = 2000
// }
func EmployeeById(id int) *Employee {
if id == 1 {
return &d
}

if id == 2 {
return e
}
return &d
}

func EmployeeById2(id int) Employee {
if id == 1 {
return d
}

if id == 2 {
return *e
}
return d
}

func main() {
d.Salary = 5000
e = new(Employee)
(*e).Salary = 2000
fmt.Printf("%T ", d)
fmt.Println(d)
fmt.Printf("%T ", e)
fmt.Println(e)

a := EmployeeById(1)
b := EmployeeById(2)
a.Position = "aaa"
b.Position = "bbb"
fmt.Printf("%T ", a)
fmt.Println(a)
fmt.Printf("%T ", b)
fmt.Println(b)

c := EmployeeById2(1)
f := EmployeeById2(2)
c.Name = "ccc"
f.Name = "fff"
fmt.Printf("%T ", c)
fmt.Println(c)
fmt.Printf("%T ", f)
fmt.Println(f)
}
1
2
3
4
5
6
7
[18:58:24] onns@Onns ~/Documents/code/go/go-bible/ch4 $ go run 4.7.go
main.Employee {0 0001-01-01 00:00:00 +0000 UTC 5000 0}
*main.Employee &{0 0001-01-01 00:00:00 +0000 UTC 2000 0}
*main.Employee &{0 0001-01-01 00:00:00 +0000 UTC aaa 5000 0}
*main.Employee &{0 0001-01-01 00:00:00 +0000 UTC bbb 2000 0}
main.Employee {0 ccc 0001-01-01 00:00:00 +0000 UTC aaa 5000 0}
main.Employee {0 fff 0001-01-01 00:00:00 +0000 UTC bbb 2000 0}

  1. 导出是什么意思? ↩︎