Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。
结构体基本语法
结构体的意义就在于,结构化数据,当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。
结构体本质就是打包不同类型的变量,成一体,聚合起来,是一种聚合性的数据类型
回忆点
- 结构体变量,与其他变量的声明相比,就是多了一步:声明类型
type
- 赋值的时候,是用的冒号
- 结构体指针,在声明和赋值的时候都与结构体有点区别(与类型声明无关)
声明type
:::info 结构体类型长什么样?
:::
结构体类型长这样:struct{...}
,和int/float/...
同地位
如struct{Name string; Age int}
或者多行展示
:::info 若直接用——匿名结构体
:::
在定义一些临时数据结构等场景下还可以使用匿名结构体。说白了就是直接干- 和声明变量一起
var user struct{Name string; Age int}
- 和初始化一起
:::info 先定义结构类型,之后再用(最常用)
:::
- 怎么定义结构体类型?
➡实例- 要求
- 类型名:标识自定义结构体的名称,在同一个包内不能重复。
- 字段名:表示结构体字段名。结构体中的字段名必须唯一
- 结构体元素以及类型的定义其实就是
var name string
省去了var
变成name string
- 同一行需要
;
分隔,不同行不需要;or,
- 要求
- 省略写法
➡
(同样类型的字段也可以写在一行)
声明变量var/初始化
初始化就有两个问题:初始化为什么变量?+ 赋值与否?
已知:
一般变量 | 结构体型 指针 | ||
---|---|---|---|
声明 (由于值类型,声明=初始化为零值) | var man_1 结构体类型 |
var p2 *person |
|
初始化为零值 | 全局 var p2 = ... /局部 p2 := |
||
等式右边——person{} |
等式右边——&person{} / new(person) |
||
赋值 | 统一赋值 | ||
花括号内: 1. 使用键值对初始化 如 可部分赋值,不赋值的自动为零值 2. 使用值得列表初始化,即只写value,不写key 如 要求 1. all——必须初始化结构体的所有字段。 2. 顺序——填充顺序 = 结构体声明顺序 3. 不能混用——不能和键值初始化混用 |
|||
挨个赋值 | <font style="color:rgb(51, 51, 51);">结构体变量名.成员名 = ... </font> 如 <font style="color:rgb(51, 51, 51);">man_1.name = ... </font> 可部分赋值,不赋值的自动为零值 |
explain:
- 注意一点,以下两种赋值方式,可部分赋值,不赋值的自动为零值(只写value,不写key的赋值就不行)
- 如
<font style="color:rgb(51, 51, 51);">man_1.name = ... </font>
- 方式:
**<font style="color:rgb(51, 51, 51);">var</font>** name 结构体类型
**结构体本身也是一种类型**(和int, float, string 一样,只是有自定义的成分),我们可以像声明内置类型一样使用var关键字声明结构体类型。
结构体是否需要实例化?
- 一个作者: 只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。
结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型。 - 我的代码发现
1. 但是var声明这种形式,普通变量就可以
2. 但是指针,必须要赋予地址才能使用
结构体型指针
- 声明/初始化上文已经说明
- 其中
[new(person)](https://www.yuque.com/drayz/gagpyy/psgg2s#c36HE)
是将类型转化为其类型的指针,并赋予底层数值为零值,的函数 - 使用要求
定义一个指针,无论是普通指针还是结构体指针,必须赋予地址/至少初始化为零值后,才可调用var prt_1 *person; prt_1 = &man_1
- 左边——全局
var p2 = ...
/局部p2 :=
右边——&person{}
/new(person)
- 其中
或者直接初始化
- 使用
- 使用注意点
在Go语言中支持,对结构体指针直接使用,来访问结构体的成员。即**<font style="color:rgb(36, 41, 46);">prt.name</font>**
和**<font style="color:rgb(36, 41, 46);">(*prt).name</font>**
完全等价(也就访问结构体成员的时候是这样),访问结构体的成员只支持**<font style="color:rgb(36, 41, 46);">.</font>**
运算符
- 使用注意点
var p2 = new(person)
fmt.Printf("%T\n", p2) //*main.person
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}
p2.name = "测试"
p2.age = 18
p2.city = "北京"
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"测试", city:"北京", age:18}
2. 打印
fmt.Println
可直接打印
!结构体的可见性/导出
- 大写开头名字:如果想别的包的函数、定义好的结构体类型、结构体成员可以在本包被调用,那么函数名、类型名、结构体成员变量名,都必须大写;
- 小写开头只能本包使用
- 如果存在嵌套结构体, - 嵌套结构体(父亲)必须在本包定义,不可用外部文件的 - 只要嵌套结构体大写,就可以用父系的字段
实例
调用
成员运算符=点号<font style="color:rgb(51, 51, 51);">.</font>
,格式为:<font style="color:rgb(51, 51, 51);">结构体名.成员名</font>
与c语言的差别
:::info c语言定义结构体和结构体变量
:::
C语言的结构:
- 类型定义/声明/初始化
- 结构类型声明(无/变量别名)——且类型的定义和名字是一起的
➡ - 声明成什么变量?——一般变量/数组
很少结构型指针
:::info 有哪些不一样呢?
:::
- 顺序
变量名与类型的顺序是反的 ,且go需要加上关键字type/var,c不需要- c:
类型➡ 变量名➡=内容
;是个色老头,先看是女的,再问名 - go:
关键字➡ 变量名➡ 类型➡=内容
先问同学你什么名,再问男的还是女的
- c:
- 关键字不同,
- c语言的关键字是类型;
- go是type和var/
:=
- 数值
- go语言,无论是类型还是,类型带不带头部
- 分号
;
c语言 | ||
---|---|---|
关键字不同 | 不管是结构体类型声明还是结构体变量定义,都是struct开头 | 1. 类型定义type 2. 变量定义 var /:= |
结构体类型的样子 | 无单独数值 | |
结构体变量的样子 | 无单独数值 | |
定义结构体类型 | 类型struct➡类型名person➡内容 |
type关键字➡类型名person➡内容 |
SqlList_dynamic L; 用typedef 关键字更容易实现 |
||
匿名结构体定义 | ||
定义结构体变量 | 关键字还是struct |
<font style="color:#c7773e;">var </font>man_1 <font style="color:#6fafbd;">person</font> |
结构体变量初始化 | 按序给值,没有 **:** 的一 一对应 |
|
修改 | c语言是不能man_1 = {....} 只有初始化有 struct person man_1 ={...} 不像go语言,数值还带上类型,c语言没有单独这样结构体数值 只能 man_1.name = ... |
go语言支持 man_1 = person{...} 也支持 man_1.name = ... |
完整过程 |
原理
结构体内存布局
type test struct {
a int8
b int8
c int8
d int8
}
n := test{
1, 2, 3, 4,
}
fmt.Printf("n.a %p\n", &n.a)
fmt.Printf("n.b %p\n", &n.b)
fmt.Printf("n.c %p\n", &n.c)
fmt.Printf("n.d %p\n", &n.d)
连续内存!!!
应用
结构体比较和赋值
- 结构体是值类型,可以进行比较,要求是类型和值都要完全一样
- 要同类型的结构体,才可相互赋值
结构体作为函数参数
:::info 值传递
:::
和其他值类型数据一样,值传递,形参无法改变实参的值
:::info 地址传递
:::
方法:传递结构体的地址+形参为结构体指针➡形参,操作的是指针所指向的内存