结构体元素

结构体由关键字·结构体类型名称和具体对象构成,如下: elements

结构体初步认识

下面通过对比数组来了解一下结构体:

  1. 从存储类型来看

数组只能存储相同的类型: s:=[]string{“a”, “b”, “c”, “d”, “e”} 结构体可以存储不同的类型

//声明结构体
type employee struct{
    name,address string
    age int
    height,weight float64
}
  1. 从内存来看

他们都是在内存中占据连续的内存空间,但对于数组来说,每一个元素所占用的内存大小都是相同的,而结构体每一项所占用的内存大小不一定相同

RAMcondition

  1. 从类型组合角度看

数组没有组合的用法,例如一个一维数组,一旦数组类型确定就可以再把另一个一维数组设置为元素值,例如:

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
}

我们很自然地通过组合的方式,把一维扩张到二维,把二维扩张到三维,把三维扩张到四维,以此类推。。。。。

  1. 从操作的角度来看

数组元素的操作是通过下标来完成的:

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来获取
  1. 从比较结构上来看

数组与结构体类似,若判断两个数组是否相同,需要看数组的存储类型,数组长度,每一个元素是否相等,若判断两个结构体是否相同,需要看结构体的类型是否相同,然后看顺序,项的名称,项的类型等等

结构体的初始化

关于数组的初始化可参见这里,相对数组,结构体的初始化有点繁杂,下面一一道来:

  1. 空结构体

所谓的空结构体,即结构体的成员为空,如下:

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,所以打印出该结果。其实说白了这就是结构体成员的缺省值问题具体如下图:

emplyeeinite

  1. 结构体的初始化

结构体的成员初始化是通过操作成员对象来完成

func main(){
    emp:=employee{}
    fmt.Println(emp)

    emp.name="tmac33"
    emp.age=25
    fmt.Println(emp)
}

采用变量+“.”+成员名=值的形式对结构体进行初始化,例如emp.age=38。这种初始化形式很类似C++、Java,那么是否还有其它形式呢?当然,以前说过GO语言就是人的正常思维语言,只要你能想到,基本上就可以正常执行 :)

emp1:=employee{"keji", "xian", 19, 183, 65}
fmt.Println(emp1)

这种初始化形式看起来更直观。

读者可能还会问,我只想对其中某几个成员赋值,而上面是对所有成员赋值,还如何办?

emp2 := employee{address: "xian", age: 20}

fmt.Println(emp2)

这样只有被指定赋值的成员才能得到真实的值,而未指定赋值的成员则被系统赋予缺省值,这种情况也被称为采用字面值进行初始化

  1. 嵌套结构体

这个比较好理解,即结构体里面嵌套结构体,我们把“身高”,“体重” 定义为一个结构体,而“身高”、“体重”是一个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 = “tmac33”

man.address = “xian”

但是怎么对嵌套的结构体成员height、weight进行初始化呢?用过面向对象编程的人很容易想到,采用如下方式:

func main(){
    man:=human{}
    man.name="tmac33"
    man.address="xian"

    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

即直接对其成员赋值,这种方式简单直接,但会引入“成员可见性”的概念

  1. 结构体成员的可见性

任何语言在代码面前都是苍白的,敏捷有一个思想就是代码胜过文档,费话少说,用代码来解释什么是“结构体成员的可见性”

// 生物会笑、会哭,所以有哭、笑成员

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),内层的成员才变的可见

  1. 再谈嵌套结构体的初始化

以上例来说,我们可以采用字面值的形式初始化:

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"
}

还可以简化为:

women:=human{
    biology:biology{
        "biology cry", "biology laugh"
    },
    cry: "cry", 
    laugh: "laugh"
}

还可以简化为:

women:=human{
    {"biology cry", "biology laugh"}, 
    "cry", 
    "laugh"
}

此时就会抛出如下异常:

error

你如果够仔细的话,就能发现嵌套结构体就只写了一个结构体类型名,而没有采用value valueType的形式,所以针对这种情况,GO语言认为内部嵌套结构体名称和类型名是同一个