[Go 入门] 第六章 使用结构体和指针
Go 入门系列参考于互联网资料与 人民邮电出版社 《Go 语言入门经典》 与 《Effective Go》,编写目的在于学习交流,如有侵权,请联系删除
结构体是由数据元素组成的构件,是一个很有用的编程构件, 本文内容:
结构体是什么
结构体是一系列具有指定数据类型的数据字段,它能够通过单个变量引用一系列相关的值。通过使用结构体,可在单个变量中储存众多类型不同的数据字段。储存在结构体中的值可轻松地访问和修改,这提供了一种灵活的数据结构创建方式。通过使用结构体,可提高模块化程序,还能创建并传递复杂的结构数据
1 |
|
对上面代码的解读为:
- 关键字 type 指定一种新类型
- 将新类型的名称指定为 Movie
- 类型名右边是数据类型,这里为结构体
- 在大括号内,使用名称和类型指定了一系列数据字段。请注意,此时没有给数据字段复制。可将结构体视为模板
- 在 main 函数中,使用简短变量赋值声明并初始化了变量m,给数据字段指定的值为相应的数据类型
- 使用点表示法访问数据字段并将其打印到控制台
创建结构体
声明结构体后,就可以通过多种方式创建,如已声明一个结构体,那么就可直接声明这种类型的变量
1 |
|
这样就创建好了结构体,并将各个数据字段设置为相应数据类型的零值。要调试和查看结构体的值,可以使用占位符 %+v
这将把字段名和值打印到在终端。创建结构体时,如没有初始化,则 Go 将把每个数据字段设置为相应数据类型的零值
{Name: Rating:0}
以这种方式创建的结构体,还可以用点表示法进行字段赋值
1 |
|
结构体数据字段的值时可变的,这意味着可动态地修改它们。一旦结构体被声明或者实例被创建,就不能再修改其字段的数据类型了。
也可以使用关键字new
来创建结构体实例。关键字new
创建结构体 Movie 的一个实例(为其分配内存);将这个结构体实例赋给变量 m 后,就可以像前面那样使用点表示法给数据字段赋值了
1 |
|
还可以通过简短变量赋值来创建结构体实例,此时可省略关键字new
。创建结构体时,可同时给字段赋值,方式是使用字段名、冒号和字段值
c := Movie{Name: "Ela", Rating: 0.8}
也可忽略字段名,按字段声明顺序给它赋值,但出于可维护性考虑,不推荐这样做
c := Movie{"Ela", 0.8}
字段很多时,让每个字段独占一行能够提高代码的可维护性和可读性,如果这样做,需要在最后一个数据字段所在行也以逗号结尾
1 |
|
使用简短变量赋值是最常用的结构体创建方式,也是推荐的方式
提示:
类C语言也支持结构体,结构体并非创建面向对象代码的方式,而是一种数据结构创建方式,旨在满足数据建模需求
嵌套结构体
有时候,数据结构需要包含多个层级,就可以创建一个结构体嵌套另一个结构体
1 |
|
创建结构体 Superhero 时,其中将包含一个数据字段为默认值的 Address 结构体,这可改善代码的灵活性和模块性,因结构体 Address 也可用于其他地方
下面将会用简短变量赋值创建一个嵌套结构体实例
1 |
|
自定义结构体数据字段的默认值
Go 语言中的零值:
类型 | 零值 |
---|---|
布尔型 (Boolean) | false |
整型 (Integer) | 0 |
浮点型 (Float) | 0.0 |
字符串 (String) | “ “ |
指针 (Pointer) | nil |
函数 (Function) | nil |
接口 (Interface) | nil |
切片 (Slice) | nil |
通道 (Channel) | nil |
映射 (Map) | nil |
创建结构体时,如果没有给出默认的值,则根据数据类型为上述的值
Go 语言没有提供自定义默认值的内置方法,但可以通过构造函数来实现
1 |
|
代码中,不直接创建结构体 Alarm,而是使用函数 NewAlarm 来创建,从而让字段 Sound 包含自定义的默认值。这是一种技巧,而并非 Go 语言规范的组成部分
比较结构体
对结构体进行比较,要看它们的类型和值是否相同,对于类型相同的结构体,可使用相等性运算符来比较,要判断两个结构体是否相等,可以使用==
,要判断它们不等可用!=
,下面代码中,创建了两个数据字段值相等的结构体,由于它们相等,所以返回true
1 |
|
不能对两个类型不同的结构体进行比较, 否则将导致编译错误。因此,试图比较两个结构体之前,必须先确定它们的类型相同。要检查结构体的类型,可使用 Go 语言包的 reflect
1 |
|
Go 语言规定,结构体及其数据字段都可能被导出,也可能不导出。如果一个标识符的首字母是大写,那么可被导出,否则将不会导出
区分指针引用和值引用
使用结构体时,明确指针引用和值引用的区别很重要。之前有说过,数据值储存在计算机内存中。指针包含值的内存地址,这意味着,指针可以读写储存的值。创建结构体时,给数据字段分配内存并给他们指定默认值;然后返回指向内存的指针,并将其赋给一个变量,使用简短变量赋值时,将分配内存并指定默认值
a := Drink{}
复制结构体时,明确内存方面的差别很重要。将指向结构体的变量赋给另一个变量时,被称为赋值
a := b
赋值后,虽然 a 与 b 相同,但它是 b 的副本,而不是指向 b 的引用。修改 b 不会影响 a,反之亦然
值引用复制结构体
1 |
|
解读如下:
- 声明结构体类型 Drink
- 创建结构体 Drink 的一个实例,并将其赋给变量a
- 声明变量b并将a赋给它
- 修改b的数据字段Ice
指针引用复制结构体
要修改原始结构体实例包含的值,必须使用指针。指针是指向内存地址的引用,因此使用它操作的不是结构体的副本而是其本身。要获得指针,可在变量名前加上和号
1 |
|
解读如下:
- 将指向 a 的指针 (而不是 a 本身)赋给b,这是使用和号字符表示的
- 修改 b 时,将修改分配给 a 的内存,因为a和b指向相同的内存
- 打印 a 和 b 的值时,将发现它们的值相同。请注意,由于b是指针,因此必须使用星号字符对其进行引用
- 将b和a的内存地址打印到控制台,以证明它们相同
问题列表
本书前面介绍了3种创建结构体的方式,我该使用哪种呢?
推荐使用简短变量赋值方式(:=)来创建结构体。关键字
new
和变量声明的方式也合法,但不那么常用我明白了如何嵌套结构体,请问最多可以嵌套多少层呢?
对结构体嵌套层级数没有任何限制,但如果嵌套层级太多,可能昭示着使用其他数据结构是更好的选择
结构体数据字段可以是任何数据类型吗?
是的,在结构体中可以使用任何数据类型,包括自定义的类型