一、结构体
@author:韩茹
版权所有:北京千锋互联科技有限公司
1.1 什么是结构体 Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
1.2 结构体的定义和初始化 1 2 3 4 5 6 type struct_variable_type struct { member definition; member definition; ... member definition; }
一旦定义了结构体类型,它就能用于变量的声明
1 variable_name := structure_variable_type {value1, value2...valuen}
初始化结构体
1 2 3 4 5 6 7 P := person{"Tom" , 25 } P := person{age:24 , name:"Tom" } p := new (person) p.age=24
1.3 结构体的访问 访问结构体成员(访问结构的各个字段)
通过点.操作符用于访问结构的各个字段。
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 package mainimport "fmt" type Books struct { title string author string subject string book_id int } func main () { var Book1 Books var Book2 Books Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 fmt.Printf( "Book 1 title : %s\n" , Book1.title) fmt.Printf( "Book 1 author : %s\n" , Book1.author) fmt.Printf( "Book 1 subject : %s\n" , Book1.subject) fmt.Printf( "Book 1 book_id : %d\n" , Book1.book_id) fmt.Printf( "Book 2 title : %s\n" , Book2.title) fmt.Printf( "Book 2 author : %s\n" , Book2.author) fmt.Printf( "Book 2 subject : %s\n" , Book2.subject) fmt.Printf( "Book 2 book_id : %d\n" , Book2.book_id) }
运行结果:
1 2 3 4 5 6 7 8 Book 1 title : Go 语言 Book 1 author : www.runoob.com Book 1 subject : Go 语言教程 Book 1 book_id : 6495407 Book 2 title : Python 教程 Book 2 author : www.runoob.com Book 2 subject : Python 语言教程 Book 2 book_id : 6495700
1.4 结构体指针 指针指向一个结构体 也可以创建指向结构的指针。
结构体指针
1 var struct_pointer *Books
以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前
1 struct_pointer = &Book1;
使用结构体指针访问结构体成员,使用 “.” 操作符
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 package mainimport "fmt" type Books struct { title string author string subject string book_id int } func main () { var Book1 Books var Book2 Books Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 printBook(&Book1) printBook(&Book2) } func printBook ( book *Books ) { fmt.Printf( "Book title : %s\n" , book.title); fmt.Printf( "Book author : %s\n" , book.author); fmt.Printf( "Book subject : %s\n" , book.subject); fmt.Printf( "Book book_id : %d\n" , book.book_id); }
结构体实例化也可以是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" type Books struct {} func (s Books) String() string { return "data" } func main () { fmt.Printf("%v\n" , Books{}) }
1.5 结构体的匿名字段 结构体的匿名字段
可以用字段来创建结构,这些字段只包含一个没有字段名的类型。这些字段被称为匿名字段。
在类型中,使用不写字段名的方式,使用另一个类型
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 type Human struct { name string age int weight int } type Student struct { Human speciality string } func main () { mark := Student{Human{"Mark" , 25 , 120 }, "Computer Science" } fmt.Println("His name is " , mark.name) fmt.Println("His age is " , mark.age) fmt.Println("His weight is " , mark.weight) fmt.Println("His speciality is " , mark.speciality) mark.speciality = "AI" fmt.Println("Mark changed his speciality" ) fmt.Println("His speciality is " , mark.speciality) fmt.Println("Mark become old" ) mark.age = 46 fmt.Println("His age is" , mark.age) fmt.Println("Mark is not an athlet anymore" ) mark.weight += 60 fmt.Println("His weight is" , mark.weight) }
可以使用”.”的方式进行调用匿名字段中的属性值
实际就是字段的继承
其中可以将匿名字段理解为字段名和字段类型都是同一个
基于上面的理解,所以可以mark.Human = Human{"Marcus", 55, 220} 和mark.Human.age -= 1
若存在匿名字段中的字段与非匿名字段名字相同,则最外层的优先访问,就近原则
通过匿名访问和修改字段相当的有用,但是不仅仅是struct字段哦,所有的内置类型和自定义类型都是可以作为匿名字段的。
1.6 结构体嵌套 嵌套的结构体 一个结构体可能包含一个字段,而这个字段反过来就是一个结构体。这些结构被称为嵌套结构。
示例代码:
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 package mainimport ( "fmt" ) type Address struct { city, state string } type Person struct { name string age int address Address } func main () { var p Person p.name = "Naveen" p.age = 50 p.address = Address { city: "Chicago" , state: "Illinois" , } fmt.Println("Name:" , p.name) fmt.Println("Age:" ,p.age) fmt.Println("City:" ,p.address.city) fmt.Println("State:" ,p.address.state) }
1.7 提升字段 在结构体中属于匿名结构体的字段称为提升字段,因为它们可以被访问,就好像它们属于拥有匿名结构字段的结构一样。理解这个定义是相当复杂的。
示例代码:
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 package mainimport ( "fmt" ) type Address struct { city, state string } type Person struct { name string age int Address } func main () { var p Person p.name = "Naveen" p.age = 50 p.Address = Address{ city: "Chicago" , state: "Illinois" , } fmt.Println("Name:" , p.name) fmt.Println("Age:" , p.age) fmt.Println("City:" , p.city) fmt.Println("State:" , p.state) }
运行结果
1 2 3 4 Name: Naveen Age: 50 City: Chicago State: Illinois
1.8 导出结构体和字段 如果结构体类型以大写字母开头,那么它是一个导出类型,可以从其他包访问它。类似地,如果结构体的字段以大写开头,则可以从其他包访问它们。
示例代码:
1.在computer目录下,创建文件spec.go
1 2 3 4 5 6 7 package computertype Spec struct { Maker string model string Price int }
2.创建main.go 文件
1 2 3 4 5 6 7 8 9 10 11 package mainimport "structs/computer" import "fmt" func main () { var spec computer.Spec spec.Maker = "apple" spec.Price = 50000 fmt.Println("Spec:" , spec) }
目录结构如下:
src
structs
computer
spec.go
main.go
1.9 结构体比较 结构体是值类型,如果每个字段具有可比性,则是可比较的。如果它们对应的字段相等,则认为两个结构体变量是相等的。
示例代码:
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 package mainimport ( "fmt" ) type name struct { firstName string lastName string } func main () { name1 := name{"Steve" , "Jobs" } name2 := name{"Steve" , "Jobs" } if name1 == name2 { fmt.Println("name1 and name2 are equal" ) } else { fmt.Println("name1 and name2 are not equal" ) } name3 := name{firstName:"Steve" , lastName:"Jobs" } name4 := name{} name4.firstName = "Steve" if name3 == name4 { fmt.Println("name3 and name4 are equal" ) } else { fmt.Println("name3 and name4 are not equal" ) } }
运行结果
1 2 name1 and name2 are equal name3 and name4 are not equal
如果结构变量包含的字段是不可比较的,那么结构变量是不可比较的
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport ( "fmt" ) type image struct { data map [int ]int } func main () { image1 := image{data: map [int ]int { 0 : 155 , }} image2 := image{data: map [int ]int { 0 : 155 , }} if image1 == image2 { fmt.Println("image1 and image2 are equal" ) } }
2.0 结构体作为函数的参数 结构体作为函数参数使用
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 ackage main import "fmt" type Books struct { title string author string subject string book_id int } func main () { var Book1 Books var Book2 Books Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 printBook(Book1) printBook(Book2) } func printBook ( book Books ) { fmt.Printf( "Book title : %s\n" , book.title); fmt.Printf( "Book author : %s\n" , book.author); fmt.Printf( "Book subject : %s\n" , book.subject); fmt.Printf( "Book book_id : %d\n" , book.book_id); }
make、new操作
make用于内建类型(map、slice 和channel)的内存分配。new用于各种类型的内存分配 内建函数new本质上说跟其它语言中的同名函数功能一样:new(T)分配了零值填充的T类型的内存空间,并且返回其地址,即一个*T类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T的零值。有一点非常重要:new返回指针
内建函数make(T, args)与new(T)有着不同的功能,make只能创建slice、map和channel,并且返回一个有初始值(非零)的T类型,而不是*T。本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。例如,一个slice,是一个包含指向数据(内部array)的指针、长度和容量的三项描述符;在这些项目被初始化之前,slice为nil。对于slice、map和channel来说,make初始化了内部的数据结构,填充适当的值。
make返回初始化后的(非零)值。
千锋Go语言的学习群:784190273
作者B站:
https://space.bilibili.com/353694001
对应视频地址:
https://www.bilibili.com/video/av56018934
https://www.bilibili.com/video/av47467197
源代码:
https://github.com/rubyhan1314/go_advanced