数据结构和算法(Golang实现)(4)简单入门Golang-结构体和方法
结构体和方法
一、值,指针和引用
我们现在有一段程序:
package main
import "fmt"
func main() {
// a,b 是一个值
a := 5
b := 6
fmt.Println("a的值:", a)
// 指针变量 c 存储的是变量 a 的内存地址
c := &a
fmt.Println("a的内存地址:", c)
// 指针变量不允许直接赋值,需要使用 * 获取引用
//c = 4
// 将指针变量 c 指向的内存里面的值设置为4
*c = 4
fmt.Println("a的值:", a)
// 指针变量 c 现在存储的是变量 b 的内存地址
c = &b
fmt.Println("b的内存地址:", c)
// 将指针变量 c 指向的内存里面的值设置为4
*c = 8
fmt.Println("a的值:", a)
fmt.Println("b的值:", b)
// 把指针变量 c 赋予 c1, c1 是一个引用变量,存的只是指针地址,他们两个现在是独立的了
c1 := c
fmt.Println("c的内存地址:", c)
fmt.Println("c1的内存地址:", c1)
// 将指针变量 c 指向的内存里面的值设置为4
*c = 9
fmt.Println("c指向的内存地址的值", *c)
fmt.Println("c1指向的内存地址的值", *c1)
// 指针变量 c 现在存储的是变量 a 的内存地址,但 c1 还是不变
c = &a
fmt.Println("c的内存地址:", c)
fmt.Println("c1的内存地址:", c1)
}
打印出:
a的值: 5
a的内存地址: 0xc000016070
a的值: 4
b的内存地址: 0xc000016078
a的值: 4
b的值: 8
c的内存地址: 0xc000016078
c1的内存地址: 0xc000016078
c指向的内存地址的值 9
c1指向的内存地址的值 9
c的内存地址: 0xc000016070
c1的内存地址: 0xc000016078
那么a,b
是一个值变量,而c
是指针变量,c1
是引用变量。
如果&
加在变量a
前:c := &a
,表示取变量a
的内存地址,c
指向了a
,它是一个指针变量。
当获取或设置指针指向的内存的值时,在指针变量前面加*
,然后赋值,如:*c = 4
,指针指向的变量a
将会变化。
如果将指针变量赋予另外一个变量:c1 := c
,那另外一个变量c1
可以叫做引用变量,它存的值也是内存地址,内存地址指向的也是变量a
,这时候,引用变量只是指针变量的拷贝,两个变量是互相独立的。
值变量可以称为值类型,引用变量和指针变量都可以叫做引用类型。
如何声明一个引用类型的变量(也就是指针变量)呢?
我们可以在数据类型前面加一个*
来表示:
var d *int
我们以后只会以值类型,和引用类型来区分变量。
二、结构体
有了基本的数据类型,还远远不够,所以Golang
支持我们定义自己的数据类型,结构体:
// 结构体
type Diy struct {
A int64 // 大写导出成员
b float64 // 小写不可以导出
}
结构体的名字为Diy
,使用type 结构体名字 struct
来定义。
结构体里面有一些成员A
和b
,和变量定义一样,类型int64
和float64
放在后面,不需要任何符号分隔,只需要换行即可。结构体里面小写的成员,在包外无法使用,也就是不可导出。
使用结构体时:
// 新建结构体,值
g := diy.Diy{
A: 2,
//b: 4.0, // 小写成员不能导出
}
// 新建结构体,引用
k := &diy.Diy{
A: 2,
}
// 新建结构体,引用
m := new(diy.Diy)
m.A = 2
可以按照基本数据类型的样子使用结构体,上述创立的:
g := diy.Diy{
A: 2,
//b: 4.0, // 小写成员不能导出
}
是一个值类型的结构体。
你也可以使用结构体值前面加一个&
或者使用new
来创建一个引用类型的结构体,如:
// 新建结构体,引用
k := &diy.Diy{
A: 2,
}
// 新建结构体,引用
m := new(diy.Diy)
m.A = 2
引用和值类型的结构体有何区别的?
我们知道函数内和函数外的变量是独立的,当传参数进函数的时候,参数是值拷贝,函数里的变量被约束在函数体内,就算修改了函数里传入的变量的值,函数外也发现不了。
但引用类型的变量,传入函数时,虽然也是传值,但拷贝的是引用类型的内存地址,可以说拷贝了一个引用,这个引用指向了函数体外的某个结构体,使用这个引用在函数里修改结构体的值,外面函数也会发现。
如果传入的不是引用类型的结构体,而是值类型的结构体,那么会完整拷贝一份结构体,该结构体和原来的结构体就没有关系了。
内置的数据类型切片slice
和字典map
都是引用类型,不需要任何额外操作,所以传递这两种类型作为函数参数,是比较危险的,开发的时候需要谨慎操作。
三、方法
结构体可以和函数绑定,也就是说这个函数只能被该结构体使用,这种函数称为结构体方法:
// 引用结构体的方法,引用传递,会改变原有结构体的值
func (diy *Diy) Set(a int64, b float64) {
diy.A = a
diy.b = b
return
}
// 值结构体的方法,值传递,不会改变原有结构体的值
func (diy Diy) Set2(a int64, b float64) {
diy.A = a
diy.b = b
return
}
只不过在以前函数的基础上func Set(a int64, b float64)
,变成了func (diy *Diy) Set(a int64, b float64)
,只不过在函数里面,可以使用结构体变量diy
里面的成员。
上面表示值类型的结构体diy Diy
可以使用Set2
方法,引用类型的结构体diy *Diy
可以使用Set
方法。
如果是这样的话,我们每次使用结构体方法时,都要注意结构体是值还是引用类型,幸运的是Golang
操碎了心,每次使用一个结构体调用方法,都会自动将结构体进行类型转换,以适配方法。比如下面:
// 新建结构体,值
g := diy.Diy{
A: 2,
//b: 4.0, // 小写成员不能导出
}
g.Set(1, 1)
fmt.Printf("type:%T:%v\n", g, g) // 结构体值变化
g.Set2(3, 3)
fmt.Printf("type:%T:%v\n", g, g) // 结构体值未变化
// 新建结构体,引用
k := &diy.Diy{
A: 2,
}
k.Set(1, 1)
fmt.Printf("type:%T:%v\n", k, k) // 结构体值变化
k.Set2(3, 3)
fmt.Printf("type:%T:%v\n", k, k) // 结构体值未变化
结构体g
是值类型,本来不能调用Set
方法,但是Golang
帮忙转换了,我们毫无感知,然后值类型就变成了引用类型。同理,k
是引用类型,照样可以使用Set2
方法。
前面我们也说过,函数传入引用,函数里修改该引用对应的值,函数外也会发现。
结构体的方法也是一样,不过范围扩散了结构体本身,方法里可以修改结构体本身,但是如果结构体是值,那么修改后,外面的世界是发现不了的。
三、关键字 new 和 make
关键字new
主要用来创建一个引用类型的结构体,只有结构体可以用。
关键字make
是用来创建和初始化一个切片或者字典。我们可以直接赋值来使用:
e := []int64{1, 2, 3} // slice
f := map[string]int64{"a": 3, "b": 4} // map
但是这种直接赋值相对粗暴,因为我们使用时可能不知道数据在哪里,数据有多少。
所以,我们在创建切片和字典时,可以指定容量大小。看示例:
s := make([]int64, 5)
s1 := make([]int64, 0, 5)
m1 := make(map[string]int64, 5)
m2 := make(map[string]int64)
fmt.Printf("%#v,cap:%#v,len:%#v\n", s, cap(s), len(s))
fmt.Printf("%#v,cap:%#v,len:%#v\n", s1, cap(s1), len(s1))
fmt.Printf("%#v,len:%#v\n", m1, len(m1))
fmt.Printf("%#v,len:%#v\n", m2, len(m2))
运行后:
[]int64{0, 0, 0, 0, 0},cap:5,len:5
[]int64{},cap:5,len:0
map[string]int64{},len:0
map[string]int64{},len:0
切片可以使用make([],占用容量大小,全部容量大小)
来定义,你可以创建一个容量大小为5
,但是实际占用容量为0
的切片,比如make([]int64, 0, 5)
,你预留了5
个空间,这样当你切片append
时,不会因为容量不足而内部去分配空间,节省了时间。
如果你省略了后面的参数如make([]int64, 5)
,那么其等于make([]int64, 5,5)
,因为这时全部容量大小就等于占用容量大小。内置语言cap
和len
可以查看全部容量大小,已经占用的容量大小。
同理,字典也可以指定容量,使用make([],容量大小)
,但是它没有所谓的占用容量,它去掉了这个特征,因为我们使用切片,可能需要五个空白的初始值,但是字典没有键的情况下,预留初始值也没作用。省略容量大小,表示创建一个容量为0
的键值结构,当赋值时会自动分配空间。
四、内置语法和函数,方法的区别
函数是代码片段的一个封装,方法是将函数和结构体绑定。
但是Golang
里面有一些内置语法,不是函数,也不是方法,比如append
,cap
,len
,make
,这是一种语法特征。
语法特征是高级语言提供的,内部帮你隐藏了如何分配内存等细节。
系列文章入口
我是陈星星,欢迎阅读我亲自写的 数据结构和算法(Golang实现),文章首发于 阅读更友好的GitBook。
- 数据结构和算法(Golang实现)(1)简单入门Golang-前言
- 数据结构和算法(Golang实现)(2)简单入门Golang-包、变量和函数
- 数据结构和算法(Golang实现)(3)简单入门Golang-流程控制语句
- 数据结构和算法(Golang实现)(4)简单入门Golang-结构体和方法
- 数据结构和算法(Golang实现)(5)简单入门Golang-接口
- 数据结构和算法(Golang实现)(6)简单入门Golang-并发、协程和信道
- 数据结构和算法(Golang实现)(7)简单入门Golang-标准库
- 数据结构和算法(Golang实现)(8.1)基础知识-前言
- 数据结构和算法(Golang实现)(8.2)基础知识-分治法和递归
- 数据结构和算法(Golang实现)(9)基础知识-算法复杂度及渐进符号
- 数据结构和算法(Golang实现)(10)基础知识-算法复杂度主方法
- 数据结构和算法(Golang实现)(11)常见数据结构-前言
- 数据结构和算法(Golang实现)(12)常见数据结构-链表
- 数据结构和算法(Golang实现)(13)常见数据结构-可变长数组
- 数据结构和算法(Golang实现)(14)常见数据结构-栈和队列
- 数据结构和算法(Golang实现)(15)常见数据结构-列表
- 数据结构和算法(Golang实现)(16)常见数据结构-字典
- 数据结构和算法(Golang实现)(17)常见数据结构-树
- 数据结构和算法(Golang实现)(18)排序算法-前言
- 数据结构和算法(Golang实现)(19)排序算法-冒泡排序
- 数据结构和算法(Golang实现)(20)排序算法-选择排序
- 数据结构和算法(Golang实现)(21)排序算法-插入排序
- 数据结构和算法(Golang实现)(22)排序算法-希尔排序
- 数据结构和算法(Golang实现)(23)排序算法-归并排序
- 数据结构和算法(Golang实现)(24)排序算法-优先队列及堆排序
- 数据结构和算法(Golang实现)(25)排序算法-快速排序
- 数据结构和算法(Golang实现)(26)查找算法-哈希表
- 数据结构和算法(Golang实现)(27)查找算法-二叉查找树
- 数据结构和算法(Golang实现)(28)查找算法-AVL树
- 数据结构和算法(Golang实现)(29)查找算法-2-3树和左倾红黑树
- 数据结构和算法(Golang实现)(30)查找算法-2-3-4树和普通红黑树
数据结构和算法(Golang实现)(4)简单入门Golang-结构体和方法的更多相关文章
- 数据结构和算法(Golang实现)(1)简单入门Golang-前言
数据结构和算法在计算机科学里,有非常重要的地位.此系列文章尝试使用 Golang 编程语言来实现各种数据结构和算法,并且适当进行算法分析. 我们会先简单学习一下Golang,然后进入计算机程序世界的第 ...
- 数据结构和算法(Golang实现)(2)简单入门Golang-包、变量和函数
包.变量和函数 一.举个例子 现在我们来建立一个完整的程序main.go: // Golang程序入口的包名必须为 main package main // import "golang&q ...
- 数据结构和算法(Golang实现)(3)简单入门Golang-流程控制语句
流程控制语句 计算机编程语言中,流程控制语句很重要,可以让机器知道什么时候做什么事,做几次.主要有条件和循环语句. Golang只有一种循环:for,只有一种判断:if,还有一种特殊的switch条件 ...
- 数据结构和算法(Golang实现)(5)简单入门Golang-接口
接口 在Golang世界中,有一种叫interface的东西,很是神奇. 一.数据类型 interface{} 如果你事前并不知道变量是哪种数据类型,不知道它是整数还是字符串,但是你还是想要使用它. ...
- 数据结构和算法(Golang实现)(6)简单入门Golang-并发、协程和信道
并发.协程和信道 Golang语言提供了go关键字,以及名为chan的数据类型,以及一些标准库的并发锁等,我们将会简单介绍一下并发的一些概念,然后学习这些Golang特征知识. 一.并发介绍 我们写程 ...
- 数据结构和算法(Golang实现)(7)简单入门Golang-标准库
使用标准库 一.避免重复造轮子 官方提供了很多库给我们用,是封装好的轮子,比如包fmt,我们多次使用它来打印数据. 我们可以查看到其里面的实现: package fmt func Println(a ...
- 六、golang中的结构体和方法、接口
结构体: 1.用来自定义复杂数据结构 2.struct里面可以包含多个字段(属性) 3.struct类型可以定义方法,注意和函数的区分 4.strucr类型是值类型 5.struct类型可以嵌套 6. ...
- FFmpeg源代码简单分析:结构体成员管理系统-AVOption
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
- FFmpeg源代码简单分析:结构体成员管理系统-AVClass
===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...
随机推荐
- 学习scrapy爬虫框架的一些经验和教训
首先python的scrapy框架很好,功能强大,使用起来也很方便,省去了很多造轮子的时间.在学习的过程中也碰到了一些问题,在这里希望能分享与大家分享,做一个参考 1.安装(pip延时响应问题) sc ...
- Journal of Proteome Research | Quantitative Subcellular Proteomics of the Orbitofrontal Cortex of Schizophrenia Patients (精神分裂症病人眶额叶皮层亚细胞结构的定量蛋白质组学研究)(解读人:王聚)
期刊名:Journal of Proteome Research 发表时间:(2019年10月) IF:3.78 单位: 里约热内卢联邦大学 坎皮纳斯州立大学 坎皮纳斯州立大学神经生物学中心 卡拉博大 ...
- 深入理解JAVA字符串常量池
初学JAVA时,在学习如何比较两个字符串是否相等,大量资料告诉我,不能用等于号( = )去比较,需要使用equals方法,理由是String是一个对象,等号此时比较的是两个字符串在java内存堆中的地 ...
- VirtualBox 安装 Centos8 使用 Xshell 连接
1.下载CentOS CentOS下载地址:https://wiki.centos.org/Download 这里选择本地安装包,网络安装包在安装时需要在线下载资源比较慢 2.安装VirtualBox ...
- (2)Windows PowerShell使用
什么是PowerShell: Windows PowerShell 是一种命令行外壳程序和脚本环境,使命令行用户和脚本编写者可以利用 .NET Framework 的强大功能.PowerShell是命 ...
- jq 导航跟随 模拟京东手机端
想做一个导航跟随,但是代码都要下载,自己简单些了一个,css都放html里面了,所以也不用下载直接新建html,然后粘贴,点开就是导航跟随效果 效果如图 <!DOCTYPE html> & ...
- C++ STL 常用容器之 stack
C++ STL Container--Stack 栈是一种先进后出(LIFO)的数据结构,限制只能在一端完成插入和删除操作,这一端叫做栈顶(top),另一端即为栈底(bottom).C++ STL(S ...
- Spring - 数据库开发概述
Spring 数据库开发 Spring 的 JDBC 模块负责数据库资源管理和镨误处理,大大简化了开发人员对数据库的操作,使得开发人员可以从繁琐的数据库操作中解脱出来,从而将更多的精力投入到编写业 ...
- .Net Core 跨平台开发实战-服务器缓存:本地缓存、分布式缓存、自定义缓存
.Net Core 跨平台开发实战-服务器缓存:本地缓存.分布式缓存.自定义缓存 1.概述 系统性能优化的第一步就是使用缓存!什么是缓存?缓存是一种效果,就是把数据结果存在某个介质中,下次直接重用.根 ...
- 3.26java作业
1.编写程序, 输入变量x的值,如果是1,输出x=1,如果是5,输出x=5,如果是 10,输出 x=10,除了以上几个值,都输出x=none.(知识点:if条件语句) package fda; imp ...