目录

  • 1、什么是结构体
  • 2、结构体数据的作用
  • 3、声明
  • 4、方法
  • 4.1 点操作符
  • 4.2 取址
  • 5、结构体字面值
  • 6、结构体的比较
  • 7、结构体的嵌入和匿名成员
  • 7.1 嵌套
  • 7.2 匿名成员
  • 8、打印方式

1、什么是结构体

1、结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体

2、每个值称为结构体的成员


2、结构体数据的作用

1、作为一个整体,就像一个变量被调用

2、作为函数的返回值

3、作为函数的参数

4、作为容器类型数据的元素,如数组

注意:

1、结构体可以作为函数的参数和返回值,考虑到效率问题,较大的结构体通常会用指针的方式传入和返回

2、如果要在函数内部修改结构体成员的话,用指针传入是必须的;因为在Go语言中,所有的函数参数都是值拷贝传入的,函数参数将不再是函数调用时的原始变量


3、声明

1、给结构体类型定义别名

2、使用该名字的结构体类型定义结构体变量

3、一般一行对应一个结构体成员,成员的名字在前类型在后

4、相邻的成员类型如果相同的话可以被合并到一行,用逗号隔开

5、输入顺序也有重要的意义,顺序不同构成不同的结构体类型

6、符合go语言导出规则,若成员名字是大写字母开头,那么该成员就是可导出的

7、一个命名为S的结构体类型将不能再包含S类型的成员:因为一个聚合的值不能包含它自身

8、S类型的结构体可以包含*S指针类型的成员,这可以让我们创建递归的数据结构,比如链表和树结构等

如下:

type Employee struct {
    ID        		int
    Name,Address    string
    DoB       		time.Time
    Position  		string
    Salary    		int
    ManagerID 		int
}

var dilbert Employee


其他特点:

1、结构体类型的零值是每个成员都是零值,通常会将零值作为最合理的默认值

2、如果结构体没有任何成员的话就是空结构体,写作struct{}。它的大小为0,也不包含任何信息,但是有时候依然是有价值的

3、针对导出规则:在外部包中不能导出结构体变量后,只能访问导出的成员,无法访问未导出的成员

4、因为结构体通常通过指针处理,可以用下面的写法来创建并初始化一个结构体变量,并返回结构体的地址:

pp := &Point{1, 2}  // 可以直接在表达式中使用,比如一个函数调用

// 等价于
pp := new(Point)
*pp = Point{1, 2}


4、方法

4.1 点操作符

1、结构体变量的成员可以通过点操作符访问

2、结构体的成员也是变量,可以直接操作成员

如:

dilbert.Salary -= 5000


4.2 取址

可以对结构体成员取址,然后通过指针访问

position := &dilbert.Position
*position = "Senior " + *position

点操作符与结构体指针变量一起操作,相当于先对结构体取值,再访问成员

var employeeOfTheMonth *Employee = &dilbert
employeeOfTheMonth.Position += " (proactive team player)"

// 相当于
(*employeeOfTheMonth).Position += " (proactive team player)"


5、结构体字面值

即给结构体初始化指定成员的值

必须遵循形状类型声明时的结构

形式一:按照定义的顺序,依次为每个成员指定一个字面值

type Point struct{ X, Y int }

p := Point{1, 2}


形式二:以成员名字和相应的值来初始化,可以包含部分或全部的成员

type Point struct{ X, Y int }

p := Point{X:1, Y:2}

注意:两种方式不能混合使用


6、结构体的比较

由于结构体成员的类型没有限制,因此结构体是否可以比较取决于成员是否可比较

1、如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,可以使用==或!=运算符比较

2、相等比较运算符==将比较两个结构体的每个成员

3、可比较的结构体类型和其他可比较的类型一样,可以用于map的key类型

type address struct {
    hostname string
    port     int
}

hits := make(map[address]int)
hits[address{"", 443}]++

7、结构体的嵌入和匿名成员

7.1 嵌套

指的是一个命名的结构体作为另一个命名的结构体类型的成员

如:分别定义一个圆和轮结构体

type Circle struct{
    X, Y, Radius int
}

type Wheel struct{
    X, Y, Radius,Spokes int
}

为避免代码冗余,可以使用嵌套方式定义:

type Point struct {
    X, Y int
}

type Circle struct {
    Center Point
    Radius int
}

type Wheel struct {
   	Circle Circle
    Spokes int
}

//  访问成员的方式,类似于python中的类,使用点操作
var w Wheel
w.Circle.Center.X = 8
w.Circle.Center.Y = 8
w.Circle.Radius =  4
w.Spokes = 20

// 字面值的方式,下面两种方式是等价的
w1 = Wheel{Circle{Point{8, 8}, 5}, 20}
w2 = Wheel{
    Circle: Circle{
        Point:  Point{X: 8, Y: 8},
        Radius: 5,
    },
    Spokes: 20, // NOTE: trailing comma necessary here (and at Radius)
}


7.2 匿名成员

嵌套定义一个结构体类型,使得对被嵌套的结构体成员的访问变得稍显麻烦,go提供的匿名成员,可以解决这个问题


匿名成员

Go语言有一个特性让我们只声明一个成员对应的数据类型而不指名成员的名字,这类成员就叫匿名成员

匿名成员的数据类型必须是命名的类型或指向一个命名的类型的指针


特点

1、匿名成员仍可以访问,它的名字就是命名的类型名

2、匿名成员的名字在点操作符中是可选的,因此在访问子成员的时候,可以忽略任何匿名成员的名字

3、使用字面值赋值初始化时,仍然需要使用名字


使用匿名成员进行结构体的嵌套定义

type Point struct {
    X, Y int
}

type Circle struct {
    Point  // 匿名成员
    Radius int
}

type Wheel struct {
    Circle  // 匿名成员
    Spokes int
}

// 访问成员变得简洁
var w Wheel
w.X = 8
w.Y = 8
w.Radius = 4
w.Spokes = 20

// 字面值赋值,仍要使用名字,不可使用下面的方式
w = Wheel{8, 8, 5, 20}                       // compile error: unknown fields
w = Wheel{X: 8, Y: 8, Radius: 5, Spokes: 20} // compile error: unknown fields


8、打印方式

Printf

%v参数包含的副词#,它表示用和Go语言类似的语法打印值

%v参数包含的副词+,它表示打印值和成员名

p := Point{X:1,Y:2}
c := Circle{Center:Point{X:2,Y:3},Radius:4}
fmt.Printf("%+v\n%#v",p,c)

/*
{X:1 Y:2}
main.Circle{Center:main.Point{X:2, Y:3}, Radius:4}
*/


Println

只打印值

p := Point{X:1,Y:2}
c := Circle{Center:Point{X:2,Y:3},Radius:4}
fmt.Println(p,c)

/*
{1 2} {{2 3} 4}
*/