《Go程序设计语言》学习笔记之结构体

一. 环境

  Centos8.5, go1.17.5 linux/amd64

二. 概念

结构体是将零个或者多个任意类型的命名变量组合在一起的聚合数据类型。每个变量都叫做结构体的成员。

三. 声明/初始化

1) 定义一个结构体类型 Human,然后通过它定义一个命名结构体变量 child。

  8 type Human struct {
9 Name string
10 Age int
11 Height float64
12 }
13
14 var child Human
15
16 func main() {
17 child.Name = "Jim"
18 child.Height = 90.0
19 child.Age = 4
20
21 fmt.Printf("%#v\n", child)
22 }

  运行结果如下

结构体的成员变量通常一行写一个,变量的名称在类型的前面,但是相同类型的连续成员变量可以写在同一行上。

成员变量的顺序对于结构体同一性很重要,如果我们调整了成员的顺序,那么我们就在定义一个不同的结构体类型。

如果一个结构体的成员变量名称是首字母大写的,那么这个变量是可导出的,这个是Go最主要的访问控制机制。一个结构体可以同时包含可导出和不可导出的成员变量。

命名结构体类型 S 中,不可以定义一个拥有相同结构体类型 S 的成员变量,也就是一个聚合类型不可以包含它自己。否则,编译会报错,提示“无效的递归类型”,如下

但是,结构体类型 S 中 可以定义一个 S 的指针类型,即 *S ,这样可以创建一些递归数据结构如链表、树。

2) 零值

结构体的零值由结构体成员的零值组成。通常情况下,零值是一个默认的自然的、合理的值。

 26 type Test struct {
27 Size int
28 Buffer string
29 IsBool bool
30 Mut sync.Mutex
31 }
32
33 func main() {
34 t1 := Test{}
35 fmt.Printf("%#v\n", t1)
36 }

  运行结果如下

结构体类型 Test 前面三个成员的零值,整型的零值为 0,字符串类型的成员零值为空字符串, 布尔类型的成员零值为 false,sync.Mutex类型的成员是一个可以直接使用且未锁定状态的互斥锁。

3) 通过结构体字面量来初始化结构体变量

一种方式是按照正确的顺序,为每个成员变量指定一个值,如 p1。这种方式适用于小型的大家都比较容易理解的结构体,比如 Point 类型结构体。

另一种方式是通过指定部分或全部成员变量的名称和值来初始化结构体变量,如 p2。这种方式一般用的较多。

  8 type Point struct {
9 X float64
10 Y float64
11 }
12
13 func main() {
14 p1 := Point{3.0, 4.0}
15 p2 := Point{X: 2.0, Y: 5.0}
16
17 fmt.Printf("%v\n", p1)
18 fmt.Printf("%v\n", p2)
19 }

  

初始化一个结构体变量的指针

 20     pp := &Point{5, 8}
21 fmt.Printf("%v\n", *pp)

  这个等价于

 23     pp2 := new(Point)
24 *pp2 = Point{5, 8}
25 fmt.Printf("%v\n", *pp2)

  &Point{5, 8} 这种方式可以直接使用在一个表达式中,例如函数调用。

四. 访问

结构体的每个成员都可以通过点号方式来访问,也可以获取成员变量的地址,然后通过指针来访问它。

h1 通过点号来访问成员 Age,h1 通过指针修改了成员变量 Height 的值。

 19 func main() {
20 h1 := Human{"Jim", 21, 65}
21
22 h1.Age++
23 *(&h1.Height) += 2.0
24 fmt.Printf("%#v\n", h1)
25 }

  运行结果如下

五. 比较

如果结构体的所有成员变量都可以比较,那么这个结构体就是可比较的。两个结构体的比较可以使用 == 或者 !=。== 操作符按照顺序比较两个结构体变量的成员变量。

第28和第29两行代码的语句是等价的

 26     p1 := Point{1, 2}
27 p2 := Point{2, 1}
28 fmt.Printf("%t\n", p1.X == p2.X && p1.Y == p2.Y)
29 fmt.Printf("%t\n", p1 == p2)

  可比较的类型都可以做为 map 的键类型,结构体变量 addr 作为 map 变量 acce 的键值

 19 type address struct {
20 Ip string
21 Port string
22 }
23
24 func main() {
25 addr := address{"127.0.0.1", "9090"}
26 acce := make(map[address]int)
27 acce[addr]++
28 fmt.Printf("%v\n", acce)
29 }

  运行结果如下

六. 使用

1) 作函数参数

一般使用指针传递的方式,一是出于效率的考虑,二 是当需要结构体变量时这种方式就是必需的。

值传递,调用者接收到的实参的一个副本,并不是实参的引用。

2) 结构体嵌套和匿名成员

a. 结构体嵌套,这个机制可以让我们将一个命名结构体当作另一个结构体类型的匿名成员使用,并提供了方便的访问方式。

借用书上的例子,下面示例代码,结构体中嵌套了命名结构体,访问结构体变量 w 中的成员时,使用到了中间的结构体变量。

  8 type Point struct {
9 X int
10 Y int
11 }
12
13 type Circle struct {
14 Center Point
15 Radius int
16 }
17
18 type Wheel struct {
19 Circle Circle
20 Spokes int
21 }
22
23 func main() {
24 var w Wheel
25 w.Circle.Center.X = 2
26 w.Circle.Center.Y = 3
27 w.Circle.Radius = 5
28 w.Spokes = 10
29 fmt.Printf("%#v\n", w)
30 }

  

在 Go 中,可以定义不带名称的结构体成员,只需要指定类型即可;这种结构体成员称作匿名成员。这个结构体成员必须是一个命名类型或者一个指向命名类型的指针

  8 type Point struct {
9 X int
10 Y int
11 }
12
13 type Circle struct {
14 Point
15 Radius int
16 }
17
18 type Wheel struct {
19 Circle
20 Spokes int
21 }
22
23 func main() {
24 var ww Wheel
25 ww.X = 2
26 ww.Y = 3
27 ww.Radius = 5
28 ww.Spokes = 10
29 fmt.Printf("%#v\n", ww)
30
31 var w Wheel
32 w.Circle.Point.X = 2
33 w.Circle.Point.Y = 3
34 w.Circle.Radius = 5
35 w.Spokes = 10
36 fmt.Printf("%#v\n", w)
37
38 }

  Circle 中有一个匿名成员 Point,Wheel 中有一个匿名成员 Circle。结构体变量 ww 可直接访问到需要的成员变量,而不需要指定一大串中间变量。结构体变量 w 的访问成员的方式也是正确,可以看到这种方式相对繁琐了不少。

  运行结果如下

b. 嵌套结构体的初始化

与前面一致。一种是按照顺序初始化,另一种是使用成员变量的名称与值进行初始化。

 23 func main() {
24 w := Wheel{Circle{Point{3, 4}, 5}, 10}
25 fmt.Printf("%v\n", w)
26
27 ww := Wheel{
28 Circle: Circle{
29 Point: Point{
30 X: 3,
31 Y: 4,
32 },
33 Radius: 5,
34 },
35 Spokes: 10,
36 }
37 fmt.Printf("%v\n", ww)
38 }

  两种方式打印结果一致

第31行,33行,35行最后面的逗号都需要带上,不然会编译报错。

c. 匿名成员不一定是结构体类型,任何命名类型或者指向命名类型的指针都可以。

结构体类型 MyType 中的成员 Ages 和 Weight 都是自定义的类型,Name 是内置类型,ContactInfo 是嵌套的结构体类型。

 23 type Ages int
24 type Weight float64
25
26 type ContactInfo struct {
27 Addr string
28 PhoneNum string
29 }
30
31 type MyType struct {
32 Name string
33 Ages
34 Weight
35 ContactInfo
36 }
37
38 func main() {
39 var t1 MyType
40 t1.Ages = 20
41 t1.Weight = 55
42 t1.Name = "Bob"
43 t1.Addr = "BeiJing"
44 t1.PhoneNum = "18888888888"
45 fmt.Printf("%v\n", t1)
46 }

  打印结果如下

以快捷方式访问匿名成员的内部变量同样适用于访问匿名成员的内部方法。因此,外围的结构体类型获取的不仅仅是匿名成员的内部变量,还有相关的方法。

《Go程序设计语言》学习笔记之结构体的更多相关文章

  1. c语言学习笔记之结构体存储

    今天讲讲结构体存储问题 首先,结构体简单说是对不同类型的封装,一开始我们可能会想结构体在内存中的存储的大小是直接元素的和 例如 我们可能会觉得是 结构体大小=int(4个字节)+ short(2个字节 ...

  2. C语言学习笔记--枚举&结构体

    枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法格式来声明: enum 枚举类型名字 {名字0,名字1,...,名字n}: 枚举类型名字通常并不真的使用,要用的是大括号里面的名字,因为 ...

  3. C语言学习笔记10-结构体、枚举、联合体

    C语言学习笔记10-结构体.枚举.联合体    待传

  4. contiki学习笔记---process结构体

    process,字面意义,进程,看看它的结构 struct process { struct process *next; #if PROCESS_CONF_NO_PROCESS_NAMES #def ...

  5. C#学习笔记之结构体

    1.概述 结构是一种与类相似的数据类型,不过它较类更为轻量,一般适用于表示类似Point.Rectangle.Color的对象.基本上结构能办到的类全都能办到,但在某些情况下使用结构更为合适,后面会有 ...

  6. 《PHP7底层设计与源码实现》学习笔记2——结构体对齐

    书里给了一段代码,假如有个结构体如下: struct test {     char a;     int b;     long c;     void* d;     int e;     cha ...

  7. C程序设计语言学习笔记

    在Windows下运行C语言程序 Windows下的编程工具使用 VC 6.0,下面讲解如何在VC 6.0下运行上节的"Hello, world"程序. 1) 新建Win32 Co ...

  8. 【C#学习笔记】结构体使用

    using System; namespace ConsoleApplication { struct _st { public string name; public int age; } clas ...

  9. Go语言学习笔记十: 结构体

    Go语言学习笔记十: 结构体 Go语言的结构体语法和C语言类似.而结构体这个概念就类似高级语言Java中的类. 结构体定义 结构体有两个关键字type和struct,中间夹着一个结构体名称.大括号里面 ...

  10. 2017-05-4-C语言学习笔记

    C语言学习笔记... ------------------------------------ Hello C语言:什么是程序:程序是指:完成某件事的既定方式和过程.计算机中的程序是指:为了让计算机执 ...

随机推荐

  1. Power BI 4 DAY

    目录 数据化结构 其他数据结构 列表嵌套列表 记录嵌套列表 M函数计算方式 运算符 爬取网页 数据化结构 其他数据结构 复合数据结构的列表 let source = { 1, //数值 "B ...

  2. Python 中获取文件名

    Python 获取文件名import osimport sys # ①获取当前文件名os.path.basename(__file__)# ②获取程序启动文件名os.path.basename(sys ...

  3. webgl 系列

    webgl 背景 工作所需... 目录 初识 WebGL 绘制一个点 三角形 变换矩阵和动画 渐变三角形 绘制猫 着色器语言

  4. 惠普CP1025 因转印离合器导致打印不全大片空白的问题

    问题症状 自检只打印出一部分, 后面大部分都是空白. 如果是碳盒缺粉, 应该是均匀地浅或者空白, 如果是成像鼓的问题, 应该是从上到下成条状的不均匀, 这样显示一节后空白的情况是没见过, 上网查有类似 ...

  5. Oracle SQL 注入攻击

    All about Security - SQL Injection 最近做个一个有关ORACLE数据库安全的网上研讨.我们有1300多位参与者,反馈相当丰富. I recently did a we ...

  6. 我在winform项目里使用“Windows I/O完成端口”的经验分享

    少年!看你骨骼惊奇,是万中无一的练武奇才,我这儿有本武林秘籍,见与你有缘就送你了! 如来神掌 Windows I/O完成端口是一个我至今都说不好的话题,请宽容的接受我这不是科班出身的自学成才的野生程序 ...

  7. C++ 多线程的错误和如何避免(9)

    有时候使用 std::atomic 比使用 mutexes 更高效 问题分析:使用多线程更新一些简单数据时,比如 int 型,bool 型等等,可以使用 std::atomic,这比 mutex 来得 ...

  8. 使用Java线程同步工具类CountDownLatch

    java.util.concurrent.CountDownLatch是Java并发并发编程中的线程同步工具类,基于AQS(java.util.concurrent.locks.AbstractQue ...

  9. abc模块的用法

    首先需要了解的是一个基类(父类),abc.ABCMeta.这个是用于实现抽象类的一个基础类 抽象方法的使用,在相应的方法之前一行加上@abstractmethod之后,从新的一行开始定义相应的方法.实 ...

  10. 【Azure 云服务】指标哪去了?在执行 Swap (交换生产部署和Staging部署) 操作后看不见云服务的旧指标

    问题描述 打开云服务(Cloud Service)的Metrics页面,发现过去了指标不见了? 以虚点构成无数据的图表. 问题解答 查看云服务的活动日志(Activity Logs),发现最近执行的操 ...