go语言结构体详解与代码演示
目录
- golang结构体定义
- 结构体的访问属性
- 结构体的初始化、赋值
- 方式1:
- 方式2:
- 方式3:
- 方式4:
- 全部代码
- 匿名结构体
- 结构体的匿名字段
- 结构体的构造函数
- 构造函数
- 指针构造与对象构造
- 给结构体添加方法
- 结构体是值类型
- 结构体做结构体的类型成员
- 结构体内存布局与字节对齐问题
- 内存字节对齐
- 结构体大小
- 空结构体
- 结构体切片成员的传值问题
golang结构体定义
go语言的结构体定义需要使用关键字type, 格式如下
type 结构体名 struct{成员1 类型成员2 类型。。。。。。
}
例如下面定义一个Person结构体
//定义结构体
type Person struct {name stringage intgender stringhobby []string
}
结构体的访问属性
结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。
结构体的初始化、赋值
结构体的初始化、赋值大概有四种方式,
方式1:
//结构体定义方法 1
var p Person
p.name = "zhangsan"
p.age = 20
p.gender = "男"
p.hobby = []string{"篮球", "皮球", "双色球"}//打印结构体内容
fmt.Println(p)//访问字段
fmt.Println(p.name)
访问字段直接用结构体变量点出来即可
方式2:
//结构体定义方法 2
//new结构体
p2 := new(Person)
p2.age = 30
p2.name = "zhaoliu"
fmt.Printf("p2 type=%T \n", p2)
fmt.Printf("p2=%p \n", p2)
方式3:
//结构体定义方法 3
//声明时进行初始化
//注意需要使用逗号
p3 := Person{name: "tianqi",age: 17,
}
fmt.Println("p3=", p3)
方式4:
//结构体定义方法 4
//值列表初始化
//必须严格按照结构体里成员的顺序, 这种需要把所有字段都进行赋值,不然无法编译
p4 := Person{"zhangwuji",20,"男",[]string{"乾坤大挪移", "九阳神功", "太极拳"},
}
fmt.Println("p4=", p4)
全部代码
package main
import "fmt"//定义结构体
type Person struct {name stringage intgender stringhobby []string
}func main() {//结构体定义方法 1var p Personp.name = "zhangsan"p.age = 20p.gender = "男"p.hobby = []string{"篮球", "皮球", "双色球"}//打印结构体内容fmt.Println(p)//访问字段fmt.Println(p.name)//结构体定义方法 2//new结构体p2 := new(Person)p2.age = 30p2.name = "zhaoliu"fmt.Printf("p2 type=%T \n", p2)fmt.Printf("p2=%p \n", p2)//结构体定义方法 3//声明时进行初始化//注意需要使用逗号p3 := Person{name: "tianqi",age: 17,}fmt.Println("p3=", p3)//结构体定义方法 4//值列表初始化//必须严格按照结构体里成员的顺序, 这种需要把所有字段都进行赋值,不然无法编译p4 := Person{"zhangwuji",20,"男",[]string{"乾坤大挪移", "九阳神功", "太极拳"},}fmt.Println("p4=", p4)//匿名结构体//不需要名字var s struct {x inty int}s.x = 100s.y = 100// 打印结构体值用%vfmt.Printf("s type = %T, s vale %v", s, s)
}
匿名结构体
可以在函数里直接定义结构体类型,不需要使用type关键字,这种称为匿名结构体.
//匿名结构体
//不需要名字
var s struct {x inty int
}
s.x = 100
s.y = 100
// 打印结构体值用%v
fmt.Printf("s type = %T, s vale %v", s, s)
结构体的匿名字段
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。
//Person 结构体
type Person struct {stringint
}func main() {p1 := Person{"小王子",18,}fmt.Printf("%#v\n", p1) //main.Person{string:"北京", int:18}fmt.Println(p1.string, p1.int) //北京 18
}
注意:这里匿名字段的说法并不代表没有字段名,而是默认会采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。
结构体的构造函数
go语言没有像C++、java、c#这种面向对象的特性,在结构体里只能声明变量,不能声明方法,为了构造结构体更方便,可以在同一个包的外面给结构体声明方法,例如可以写构造函数:
package main
import "fmt"type Student struct {id intname string
}func newStudent(_id int, _name string) Student {return Student{_id, _name,}
}func newStudentPtr(_id int, _name string) *Student {return &Student{_id, _name,}
}//给结构体添加方法
func (this Student) sayHello() {fmt.Printf("我的名字是%s, 学号%d\n", this.name, this.id)
}//值接收
func (this Student) changeId() {this.id++
}//指针接收者
func (this *Student) changeIdYes() {this.id++
}func main() {p1 := newStudent(1001, "张三")fmt.Println(p1)p1.sayHello()p1.changeId()fmt.Printf("id = %d\n", p1.id)p1.changeIdYes()fmt.Printf("id = %d\n", p1.id)p2 := newStudentPtr(1002, "李四")fmt.Println(p2)
}
例如上面的代码,声明了一个Student结构体,newStudent, newStudentPtr是两个构造函数,go语言没有方法重载的概念,所以不能有同名函数。
构造函数
func newStudent(_id int, _name string) Student {return Student{_id, _name,}
}func newStudentPtr(_id int, _name string) *Student {return &Student{_id, _name,}
}
指针构造与对象构造
当结构体成员比较多时,建议使用返回指针的形式进行构造,减少内存的开销,也就是newStudentPtr这种写法;当结构体成员比较少时,可以使用返回对象的写法,也就是newStudent这种写法。
给结构体添加方法
Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self。
格式如下
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {函数体
}
在上面的代码中,给结构体添加了3个方法
//给结构体添加方法
func (this Student) sayHello() {fmt.Printf("我的名字是%s, 学号%d\n", this.name, this.id)
}//值接收
func (this Student) changeId() {this.id++
}//指针接收者
func (this *Student) changeIdYes() {this.id++
}
注意上面的this可以随便命名,只要是合理的标识符即可,用this,主要是参考C++的写法,更形象,也可以写成self。
结构体是值类型
在方法changeId中,给id成员执行++操作,但是执行后并没有++, 如果想要用方法改变传入的值,得用指针传参,也就是changeIdYes()这种写法
结构体做结构体的类型成员
package main
import "fmt"type BaseInfo struct {id intname string
}type Student struct {gender stringbaInfo BaseInfo
}
func main() {var s1 Students1.baInfo.name = "张三"s1.baInfo.id = 1001s1.gender = "男"fmt.Println(s1)
}
上面代码是把BaseInfo作为Student的成员
变量名baInfo 也可以不写,例如
package main
import "fmt"type BaseInfo struct {id intname string
}//type Student struct {// gender string
// baInfo BaseInfo
//}type Student struct {gender stringBaseInfo
}func main() {//var s1 Student//s1.baInfo.name = "张三"//s1.baInfo.id = 1001//s1.gender = "男"var s1 Students1.name = "张三"s1.id = 1001s1.gender = "男"fmt.Println(s1)
}
省略baInfo后,就可以用s1直接访问
这种嵌套定义有点类似C++、java的类继承,不过由于golang的特点,看起来不是那么明显
结构体内存布局与字节对齐问题
go结构体有类似C/C++的内存对齐问题,例如下面的代码
package main
import "fmt"type test struct {a int8b int8c int8d int8
}func main() {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)
}
输出
n.a 0xc000128058
n.b 0xc000128059
n.c 0xc00012805a
n.d 0xc00012805b
内存字节对齐
由于是int8,站一个字节,所以此时a,b,c,d的顺序是一致的,如果换成int32就不同了,如下所示
type test struct {a int8b int32c int8d int32
}
输出结果
n.a 0xc00001a0b0
n.b 0xc00001a0b4
n.c 0xc00001a0b8
n.d 0xc00001a0bc
由于要处理内存对齐问题,虽然a是8位,占一个字节,但是它的下一个成员b是占4个字节,为了字节对齐,那么a也是得占用4个字节,c, d类似,如下图:
结构体大小
fmt.Printf("结构体大小:%d", unsafe.Sizeof(n))
例如如上面结构体大小为16
n.a 0xc0000aa070
n.b 0xc0000aa074
n.c 0xc0000aa078
n.d 0xc0000aa07c
结构体大小:16
空结构体
空结构体是不占用空间的。
var v struct{}
fmt.Println(unsafe.Sizeof(v)) // 0
结构体切片成员的传值问题
看下面的代码
package main
import "fmt"type Person struct {name stringage int8dreams []string
}func (p *Person) SetDreams(dreams []string) {p.dreams = dreams
}func main() {p1 := Person{name: "小王子", age: 18}data := []string{"吃饭", "睡觉", "打豆豆"}p1.SetDreams(data)fmt.Println(p1.dreams)// 突然修改了data[1]的值data[1] = "不睡觉"fmt.Println(p1.dreams)
}
在设置Person的成员dreams后,不小心修改了slice的值,因为slice类型包含了指向底层数据的指针,此时Person的成员dreams也会被跟着修改
输出结果
[吃饭 睡觉 打豆豆]
[吃饭 不睡觉 打豆豆]
为了避免这种情况,正确的做法是在方法中使用传入的slice的拷贝进行结构体赋值。
func (p *Person) SetDreams(dreams []string) {p.dreams = make([]string, len(dreams))copy(p.dreams, dreams)
}
这种问题存在于slice和map,在实际编码过程中一定要注意这个问题。
go语言结构体详解与代码演示相关推荐
- C语言结构体详解(结构体定义,使用,结构体大小等)
c语言结构体详解 1.c语言结构体 1.1 结构体基础知识 1.2 结构体声明 1.3 结构体特殊声明 1.4 结构体的自引用 1.5 结构体的大小的计算 1.5.1了解结构体大小计算规则 1.5.2 ...
- 计算机科学类专升本复习之“C语言结构体”详解(初稿)
C语言结构体详解,C语言struct用法详解 前面所学到的"数组":它是一组具有"相同类型"的数据的集合. 但是在实际的编程中,我们往往还需要 一组" ...
- linux中c语言结构体详解,Linux C语言结构体-学习笔记
Linux C语言结构体简介 前面学习了c语言的基本语法特性,本节进行更深入的学习. 预处理程序. 编译指令: 预处理, 宏定义, 建立自己的数据类型:结构体,联合体,动态数据结构 c语言表达式工具 ...
- 梦开始的地方 —— C语言结构体详解
文章目录 结构体 1. 结构体声明 2. 结构体的自引用 3. 结构体初始化和访问 4. 结构体成员访问 5. 结构体传参 6. 结构体内存对齐 7. 修改默认对齐数 8. 为什么存在结构体内存对齐? ...
- C语言 | 结构体详解(C结构体)
========================================== 博主github:https://github.com/MichaelBeechan 博主CSDN:https:/ ...
- c语言结构体详解(struct)
在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据.结构体的定义形式为:: struct 结构体名{ 结构体所包含的变量或数组 }; 结构体是一种集合,它里面包含了多个变量或数组,它们 ...
- 让你不再害怕结构体-C语言结构体详解
结构体:是一种复合数据类型,多种数据类型集合在一起用来描述事物的整体 与数组类似,数组是都个相同类型的数据合在一起,而结构是多个不同类型的数据合在一起. 结构体归根结底是一种数据类型,由程序员根据需要 ...
- C# 之 结构体详解
C# 之 结构体详解 一.结构体的定义 二.结构体的初始化 1.实例构造函数 2.静态构造函数 三.结构体的使用 1.赋值操作 2.作为方法参数和返回值 一.结构体的定义 概念:C#的结构体类型(或称 ...
- 【FFmpeg】结构体详解(二):AVStream、AVPacket、AVOutputFormat
FFmpeg结构体详解 7.AVStream 8.AVPacket 9.AVOutputFormat 7.AVStream AVStream 是存储每一个视频/音频流信息的结构体. 重要的变量如下所示 ...
最新文章
- l298n电机驱动模块_带DRV8825驱动器模块和Arduino的控制步进电机
- Flask项目--爱家租房项目结构图
- 厉害了,Spring中bean的12种定义方法!
- vue 项目上传到码云 解决push失败
- 官方晒一加8系列旗舰关键配置:号称更极致的屏幕机皇
- IOCP编程小结(中)
- [HNOI2008]水平可见直线 半平面交
- vscode 翻译_前端新手 VSCode 入门指南
- java字符串型断言消息_Java断言
- Aizu0189 Convenient Location【Floyd算法】
- php 循环获取分类,PHP 循环删除无限分类子节点
- 通达OA的一些资源地址,持续更新
- NOI / 2.5基本算法之搜索7834:分成互质组(详细讲解)
- 2022 美亚杯 资格赛 赛后复盘 题解
- Mask RCNN 算法 No Instances to display
- [EXCEL] 宏的录制、调用和删除
- numpy中数据不以科学记数法显示
- golang最适合(擅长)做什么
- python大数据工程师 培训_大数据工程师需要学习哪些?
- 19级赵同学推荐的十佳医疗图像处理论文
热门文章
- Java七大设计原则详解与运用
- NUIST第一届程序设计大赛团队赛题解
- 献给曾经爱过和正在爱的你
- 敏捷的诺基亚测试(Nokia Test)
- 在Unity中制作逼真的建筑场景
- 聚类算法之高斯混合模型
- 大数据技术之_11_HBase学习_03_HBase 实战之谷粒微博(练习API) + 扩展知识(布隆过滤器+HBase2.0 新特性)
- Android Studio 通过wify连接手机调试
- ASRT语音识别项目
- 手把手教你安装WordPress详细教程(图文)