《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 package mainimport ( "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 = &dilbertemployeeOfTheMonth.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 }
如果结构体成员名字是以大写字母开头的,那么该成员就是导出的。一个结构体可能同时包含导出和未导出的成员。
一个命名为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 package mainimport ( "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
是等效的?我很好奇。。。
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) id := dilbert.ID EmployeeByID(id).Salary = 0
后面的语句通过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 package mainimport ( "fmt" "time" ) type Employee struct { ID int Name string Address string DoB time.Time Position string Salary int ManagerID int } var d Employeevar e *Employeefunc 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}