结构体的使用
# 一、结构体
# 1.1 什么是结构体
Go
语言中数组可以存储同一类型的数据,但在结构体中可以为不同项定义不同的数据类型.- 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.
# 1.2 结构体的定义和初始化
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
2
3
4
5
6
- 一旦定义了结构体类型,它就能用于变量的声明
variable_name := structure_variable_type {value1, value2...valuen}
# 初始化结构体
// 1.按照顺序提供初始化值
P := person{"Tom", 25}
// 2.通过field:value的方式初始化,这样可以任意顺序
P := person{age:24, name:"Tom"}
// 3.new方式,未设置初始值的,会赋予类型的默认初始值
p := new(person)
p.age=24
2
3
4
5
6
7
# 1.3 结构体的访问
- 访问结构体成员(访问结构的各个字段)
- 通过点
.
操作符用于访问结构的各个字段.
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* 声明 Book1 为 Books 类型 */
var Book2 Books /* 声明 Book2 为 Books 类型 */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
/* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700
/* 打印 Book1 信息 */
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)
/* 打印 Book2 信息 */
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)
}
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
运行结果:
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
2
3
4
5
6
7
8
# 示例代码
package main
import "fmt"
func main() {
/*
结构体: 是由一系列具有相同类型和不同类型的数据结构成的数据集合
结构体成员是由一系列的成员变量构成,这些成员变量也被称为"字段"
*/
//1.方法一
var p1 Person
fmt.Println(p1)
p1.name = "王二狗"
p1.age = 30
p1.sex = "男"
p1.address = "上海"
fmt.Printf("姓名: %s,年龄: %d,性别: %s,地址: %s\n", p1.name, p1.age, p1.sex, p1.address)
//2.方法二
p2 := Person{}
p2.name = "Ruby"
p2.age = 28
p2.sex = "nv"
p2.address = "北京市"
fmt.Printf("姓名: %s,年龄: %d,性别: %s,地址: %s\n", p2.name, p2.age, p2.sex, p2.address)
//3.方法三
p3 := Person{name: "如花", age: 20, sex: "女", address: "杭州市"}
fmt.Println(p3)
p4 := Person{
name: "隔壁老王",
age: 40,
sex: "男",
address: "武汉市",
}
fmt.Println(p4)
//4.方法四
p5 := Person{"李小花", 25, "女", "成都"}
fmt.Println(p5)
}
//1.定义结构体
type Person struct {
name string
age int
sex string
address string
}
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
# 1.4 结构体指针
- 指针指向一个结构体
- 也可以创建指向结构的指针.
# 结构体指针
var struct_pointer *Books
以上定义的指针变量可以存储结构体变量的地址.查看结构体变量地址,可以将&
符号放置于结构体变量前
struct_pointer = &Book1;
使用结构体指针访问结构体成员,使用"."
操作符
struct_pointer.title;
package main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* Declare Book1 of type Book */
var Book2 Books /* Declare Book2 of type Book */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
/* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700
/* 打印 Book1 信息 */
printBook(&Book1)
/* 打印 Book2 信息 */
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);
}
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 main
import "fmt"
type Books struct {
}
func (s Books) String() string {
return "data"
}
func main() {
fmt.Printf("%v\n", Books{})
}
2
3
4
5
6
7
8
9
10
11
12
13
# 1.5 结构体的匿名字段
# 结构体的匿名字段
- 可以用字段来创建结构,这些字段只包含一个没有字段名的类型.这些字段被称为匿名字段.
- 在类型中,使用不写字段名的方式,使用另一个类型
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // 匿名字段,那么默认Student就包含了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)
}
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
可以使用"."的方式进行调用匿名字段中的属性值
实际就是字段的继承
其中可以将匿名字段理解为字段名和字段类型都是同一个
基于上面的理解,所以可以
mark.Human = Human{"Marcus", 55, 220}
和mark.Human.age -= 1
若存在匿名字段中的字段与非匿名字段名字相同,则最外层的优先访问,就近原则
通过匿名访问和修改字段相当的有用,但是不仅仅是struct
字段哦,所有的内置类型和自定义类型都是可以作为匿名字段的.
# 示例代码
package main
import "fmt"
func main() {
/*
匿名结构体和匿名字段:
匿名结构体: 没有名字的结构体,
在创建匿名结构体时,同时创建对象
变量名 := struct {
定义字段Field
}{
字段进行赋值
}
匿名字段: 一个结构体的字段没有字段名
匿名函数:
*/
s1 := Student{name: "张三", age: 18}
fmt.Println(s1.name, s1.age)
func() {
fmt.Println("hello world...")
}()
s2 := struct {
name string
age int
}{
name: "李四",
age: 19,
}
fmt.Println(s2.name, s2.age)
//w1 := Worker{name: "王二狗", age: 30}
//fmt.Println(w1.name, w1.age)
w2 := Worker{"李小花", 32}
fmt.Println(w2)
fmt.Println(w2.string, w2.int)
}
type Worker struct {
//name string
//age int
string //匿名字段
int //匿名字段,默认使用数据类型作为名字,那么匿名字段的类型就不能重复,否则会冲突
}
type Student struct {
name string
age int
}
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
# 1.6 结构体嵌套
# 嵌套的结构体
- 一个结构体可能包含一个字段,而这个字段反过来就是一个结构体.这些结构被称为嵌套结构.
示例代码:
package main
import (
"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)
}
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 main
import "fmt"
func main() {
/*
结构体嵌套: 一个结构体中的字段,是另一个结构体类型.
has a
*/
b1 := Book{}
b1.bookName = "西游记"
b1.price = 45.8
s1 := Student{}
s1.name = "王二狗"
s1.age = 18
s1.book = b1 //值传递
fmt.Println(b1)
fmt.Println(s1)
fmt.Printf("学生姓名: %s,学生年龄: %d,看的书是: <<%s>>,书的价格是:%.2f\n", s1.name, s1.age, s1.book.bookName, s1.book.price)
s1.book.bookName = "红楼梦"
fmt.Println(s1)
fmt.Println(b1)
b4 := Book{bookName: "呼啸山庄", price: 76.9}
s4 := Student2{name: "Ruby", age: 18, book: &b4}
fmt.Println(b4)
fmt.Println(s4)
fmt.Println("\t", s4.book)
s4.book.bookName = "雾都孤儿"
fmt.Println(b4)
fmt.Println(s4)
fmt.Println("\t", s4.book)
s2 := Student{name: "李小花", age: 19, book: Book{bookName: "Go语言是怎么炼成的", price: 89.7}}
fmt.Println(s2.name, s2.age)
fmt.Println("\t", s2.book.bookName, s2.book.price)
s3 := Student{
name: "Jerry",
age: 17,
book: Book{
bookName: "十万个为什么",
price: 55.9,
},
}
fmt.Println(s3.name, s3.age)
fmt.Println("\t", s3.book.bookName, s3.book.price)
}
//1.定义一个书的结构体
type Book struct {
bookName string
price float64
}
//2.定义学生的结构体
type Student struct {
name string
age int
book Book
}
type Student2 struct {
name string
age int
book *Book // book的地址
}
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
# 1.7 提升字段
- 在结构体中属于匿名结构体的字段称为提升字段,因为它们可以被访问,就好像它们属于拥有匿名结构字段的结构一样.理解这个定义是相当复杂的.
示例代码:
package main
import (
"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.city) //city is promoted field
fmt.Println("State:", p.state) //state is promoted field
}
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
运行结果
Name: Naveen
Age: 50
City: Chicago
State: Illinois
2
3
4
# 1.8 导出结构体和字段
- 如果结构体类型以大写字母开头,那么它是一个导出类型,可以从其他包访问它.类似地,如果结构体的字段以大写开头,则可以从其他包访问它们.
示例代码:
- 1.在
computer
目录下,创建文件spec.go
package computer
type Spec struct { //exported struct
Maker string //exported field
model string //unexported field
Price int //exported field
}
2
3
4
5
6
7
- 2.创建
main.go
文件
package main
import "structs/computer"
import "fmt"
func main() {
var spec computer.Spec
spec.Maker = "apple"
spec.Price = 50000
fmt.Println("Spec:", spec)
}
2
3
4
5
6
7
8
9
10
11
目录结构如下:
src
structs
computer
spec.go
main.go
2
3
4
5
# 1.9 结构体比较
- 结构体是值类型,如果每个字段具有可比性,则是可比较的.如果它们对应的字段相等,则认为两个结构体变量是相等的.
示例代码:
package main
import (
"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")
}
}
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
运行结果
name1 and name2 are equal
name3 and name4 are not equal
2
如果结构变量包含的字段是不可比较的,那么结构变量是不可比较的
示例代码:
package main
import (
"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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2.0 结构体作为函数的参数
- 结构体作为函数参数使用
ackage main
import "fmt"
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books /* 声明 Book1 为 Books 类型 */
var Book2 Books /* 声明 Book2 为 Books 类型 */
/* book 1 描述 */
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
/* book 2 描述 */
Book2.title = "Python 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700
/* 打印 Book1 信息 */
printBook(Book1)
/* 打印 Book2 信息 */
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);
}
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
# 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返回初始化后的(非零)值.
make函数的返回值并不是指针
# 示例代码
package main
import "fmt"
func main() {
/*
数据类型:
值类型: int,float,bool,string,array,struct
引用类型: slice,map,function,pointer
通过指针:
new(),不是nil,空指针
指向了新分配的类型的内存空间,里面存储的零值.
*/
//1.结构体是值类型
p1 := Person{"王二狗", 30, "男", "北京市"}
fmt.Println(p1)
fmt.Printf("%p,%T\n", &p1, p1)
p2 := p1 // 这里是值传递("值类型"默认是深拷贝)
fmt.Println(p2)
fmt.Printf("%p,%T\n", &p2, p2)
p2.name = "李小花"
fmt.Println(p2)
fmt.Println(p1)
//2.定义结构体指针
var pp1 *Person
pp1 = &p1
fmt.Println(pp1)
fmt.Printf("%p,%T\n", pp1, pp1)
fmt.Println(*pp1)
//(*pp1).name = "李四"
pp1.name = "王五"
fmt.Println(pp1)
fmt.Println(p1)
//使用内置函数new(),go语言汇总专门用于创建某种类型的指针的函数
pp2 := new(Person)
fmt.Printf("%T\n", pp2)
fmt.Println(pp2)
//(*pp2).name
pp2.name = "Jerry"
pp2.age = 20
pp2.sex = "男"
pp2.address = "上海市"
fmt.Println(pp2)
pp3 := new(int)
fmt.Println(pp3)
fmt.Println(*pp3)
fmt.Printf("%T\n", pp3)
}
type Person struct {
name string
age int
sex string
address string
}
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
- 01
- AWS NAT-NetWork-Firwalld配置(一)04-09
- 02
- AWS NAT-NetWork-Firwalld配置(二)04-09
- 03
- kubernetes部署minio对象存储01-18