当我第一次接触到C语言时,就对结构体投入了极大的兴趣,认为这个东西之后大有做为,后来接触Java、C++,面向对象编程中的对象进入个人视线,通过了这么多年的磨练,回过头来再看结构体依旧是那么亲切;同时从另外一个角度上看结构体与面向对象中的成员对象是多么的相像 :)
编程
1、结构体元素数组
结构体由关键字、结构体类型名称和具体成员构成,以下:ide
2、结构体初步认识spa
下面经过对比数组(复合类型)来了解一下结构体:orm
一、从存储类型来看对象
数组只能存储相同的类型:blog
s := []string{"a", "b", "c", "d", "e"}内存
结构体能够存储不一样的类型文档
// 声明结构体字符串 type employee struct { name,address string // 姓名、住址 age int // 年龄 height,weight float64 // 身高、体重 } |
二、从内存来看
它们都是在内存中占据连续的内存空间,但对于数组来讲,每个元素所占用的内存大小是相同的,而结构体每个项所占用的内存大小不必定相同
三、从类型组合角度来看
数组没有组合的用法,例如一个一维数组,一旦数组类型肯定就不能够再把另外一个一维数组设置为元素值,例如
s := []string{"a", "b", "c", "d", "e"}
s[0] = []string{"f", "g"}
此时运行该程序会出现相似此提示:cannot use []string literal (type []string) as type string in assignment;
结构体支持组合,咱们知道一维空间是一条线,二维空间是一个平面,三维空间是一个空间
type line struct { x int } type plane struct { line y int } type space struct { plane z int } |
咱们很天然地经过组合的方式,把一维扩展到二维,把二维扩展到三维,把三维扩展到四维,依次类推......
四、从操做角度上来看
数组元素的操做是经过下标来完成的:
s := []string{"a", "b", "c", "d", "e"} for i := 0; i < len(s); i++ { fmt.Println(s[i]) // 打印数组中每个元素,经过s[i]下标的方式来获取 } |
而结构体是经过项名来完成的:
t := space{plane{line{3}, 5}, 7} fmt.Println(t.x, t.y, t.z) // 经过操做结构体的项名t.x、t.y、t.z来获取 |
五、从比较角度上来看
数组与结构体相似,若判断两个数组是否相同,须要看数组的存储类型、数组长度、每个元素是否相等,一样判断两个结构体是否相同,须要看结构体的类型是否相同,而后看项的顺序、项的名称、项的类型等等
3、结构体的初始化
关于数组的初始化可参见《【6】GO语言的数组》,相对数组结构体的初始化有点繁杂,下面一一道来:
一、空结构体
所谓空结构体,即结构体的成员为空,以下:
// 声明空结构体employee type employee struct { } func main(){ emp := employee{} // 结构体的初始化,直接使用结构体类型名称后面跟一个大括号 fmt.Println(emp) } |
其中employee{}就表示初始化一个结构体,而后赋值给emp,运行就会打印出结果{},由于该结构体成员为空;可能有读者想,若结构体有成员,一样这样初始化会有什么结果呢?
type employee struct { name, address string // 姓名、住址 age int // 年龄 height, weight float64 // 身高、体重 } func main(){ emp := employee{} // 这样有什么结果呢? fmt.Println(emp) } |
运行一下就会发现,结果是{ 0 0 0},由于字符串的缺省值为空串,不会显示出来,而int和float64的缺省值为0,因此打印出该结果。其实说白了这就是结构体成员的缺省值问题,具体以下图:
二、结构体的初始化
结构体的成员初始化是经过操做成员对象来完成
func main() { emp := employee{} fmt.Println(emp) emp.name = "***eagle" emp.age = 38 fmt.Println(emp) } |
采用变量+"."+成员名=值的形式对结构体进行初始化,例如emp.age=38。这种初始化形式很相似C++、Java,那么是否还有其它形式呢?固然,之前说过GO语言就是人的正常思惟语言,只要你能想到,基本上就能够正常执行 :)
emp1 := employee{"keji", "hangzhou", 19, 175, 65} fmt.Println(emp1) |
这种初始化形式看起来更直观。上面的执行结果以下:
读者可能还会问,我只想对其中的某几个成员赋值,而上面是对全部成员赋值,该如何办呢?
emp2 := employee{address: "hangzhou", age: 20} fmt.Println(emp2) |
这样只有被指定赋值的成员才能获得真实的值,而未指定赋值的成员则被系统赋予缺省值,这种状况也被称为采用字面值进行初始化
三、嵌套结构体
这个比较好理解,即结构体里面嵌套结构体,咱们把“身高”、“体重”定义为一个结构体,而“身高”、“体重”是一个human(结构体)的成员,因此能够采用嵌套结构体:
// 体形 type figure struct { height, weight float64 } type human struct { name, address string figure } |
即human结构体中包含figure结构体,咱们能够采用下面的初始化
man := human{}
fmt.Println(man)
执行结果为{ {0 0}}
结合上面讲的结构体初始化,咱们很容易经过字面值对name和address初始化
man.name = "siyu"
man.address = "tianjin"
可是怎么对嵌套的结构体成员height、weight进行初始化呢?用过面向对象编程的人很容易想到,采用以下方式:
func main() { man := human{} man.name = "siyu" man.address = "tianjin" man.figure.height = 172.8 man.figure.weight = 175.3 fmt.Println(man) } |
即一层层向下找:先找man的成员figure,而后经过figure的成员height对身高进行赋值,这样没有问题,其实GO给咱们提供了一种更便捷的赋值方式:
man.height = 172.8 man.weight = 175.3 |
即直接对其成员赋值,这种方式简单直接,但会引入“成员可见性”的概念
四、结构体成员的可见性
任何语言在代码面前都是苍白的,敏捷有一个思想就是代码赛过文档,费话少说,用代码来解释什么是“结构体成员的可见性”
// 生物会笑、会哭,因此有哭、笑成员 type biology struct { cry, laugh string } // 人会笑、会哭,因此也有哭、笑成员;但同时人嵌套了生物结构体 type human struct { biology cry, laugh string } |
下面采用以下方式对结构体初始化
man := human{}
man.cry = "cry"
man.laugh = "laugh"
fmt.Println(man)
那么这里的man.cry,man.laugh是对human的成员赋值呢?仍是对biology的成员赋值呢?运行一下结果即可以知道,这里是对human的成员赋值
由于内层大括号是空的,为何这样呢?
能够按剥洋葱的思惟来理解,若最外层有此成员名(cry、laugh)则不用再向里面剥了,若最外层没有该成员名,则进一步向里面剥,直到找到为止;
这也就是说,若外层有成员名(cry、laugh),则内层的同名成员是不可见的,若外层没有成员名(cry、laugh),内层的成员才变的可见
五、再谈嵌套结构体的初始化
以上例来讲,咱们能够采用字面值的形式初始化:
man := human{}
man.cry = "cry"
man.laugh = "laugh"
man.biology.cry = "biology cry"
man.biology.laugh = "biology laugh"
其实还能够采用以下形式:
woman := human{biology: biology{cry: "biology cry", laugh: "biology laugh"}, cry: "cry", laugh: "laugh"}
还能够简化为:
woman := human{biology: biology{"biology cry", "biology laugh"}, cry: "cry", laugh: "laugh"}
是否还能够简化为?
woman := human{{"biology cry", "biology laugh"}, "cry", "laugh"}
此时就会抛出以下异常:
你若是够仔细的话,就能发现嵌套结构体就只写了一个结构体类型名,而没有采用value valueType的形式,因此针对这种状况,GO语言认为内部嵌套结构体名称和类型名是同一个