大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
go语言中的指针和地址值,在使用上常常具有迷惑性,主要是其特殊的*、符号的使用,可能会让你摸不透,本文希望能讲清楚go语言的指针(pointer)和值(value)。
梅江网站制作公司哪家好,找创新互联建站!从网页设计、网站建设、微信开发、APP开发、成都响应式网站建设公司等网站项目制作,到程序开发,运营维护。创新互联建站2013年至今到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选创新互联建站。
这里先简单的对指针和地址值概念做一个定义:
这是因为go方法传递参数的方式导致的,go方法函数传递参数传递的是一个拷贝,看看下面的程序会输出什么?
答案是8,而不是9,因为AddAge函数修改的是学生的一个备份,而不是原始的学生对象
如果你想正确的给学生年龄增加的话,函数传递的需要是这个值的指针,如下所示:
需要注意的是,这里我们的指针传递的仍然是一个拷贝,比如,如果你将s赋值给另外一个指针地址,不会影响原有的指针,这点可以自行实践下。
那在使用go语言开发的时候,何时该用指针何时改用地址值呢?比如考虑以下场景:
简单原则: 当你不确定该使用哪种的时候,优先使用指针
如果考虑在数组、切片、map等复合对象中使用指针和值,比如:
很多开发者会认为b会更高效,但是被传递的都是一个切片的拷贝,切片本身就是一个引用,所以这里被传递的其实没有什么区别。
对于指针和地址值的使用,大家需要牢记的一点就是go数据传递的不可变性,活学活用此特点,在无状态函数中此特性非常有用。
GO语言的优势:可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了。静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉就是有很多的包可以使用,写起来的效率很高。语言层面支持并发,这个就是Go最大的特色,天生的支持并发,我曾经说过一句话,天生的基因和整容是有区别的,大家一样美丽,但是你喜欢整容的还是天生基因的美丽呢?Go就是基因里面支持的并发,可以充分的利用多核,很容易的使用并发。内置runtime,支持垃圾回收,这属于动态语言的特性之一吧,虽然目前来说GC不算完美,但是足以应付我们所能遇到的大多数情况,特别是Go1.1之后的GC。简单易学,Go语言的作者都有C的基因,那么Go自然而然就有了C的基因,那么Go关键字是25个,但是表达能力很强大,几乎支持大多数你在其他语言见过的特性:继承、重载、对象等。丰富的标准库,Go目前已经内置了大量的库,特别是网络库非常强大,我最爱的也是这部分。内置强大的工具,Go语言里面内置了很多工具链,最好的应该是gofmt工具,自动化格式化代码,能够让团队review变得如此的简单,代码格式一模一样,想不一样都很困难。跨平台编译,如果你写的Go代码不包含cgo,那么就可以做到window系统编译linux的应用,如何做到的呢?Go引用了plan9的代码,这就是不依赖系统的信息。Go语言这么多的优势,你还不想学吗?我记得当时我看的是黑马程序员的视频,我对他们视频的印象就是通俗易懂,就是好!
于c语言相同,go中也有指针和结构体的概念。指针表示变量的内存地址,结构体用来存储同一类型的数据。
定义一个指针变量,将变量a的地址赋给指针变量p。这样,指针变量p也就指向了变量a所在的内容空间。
new 函数返回一个指针变量
fmt.scan() 就是传入一个指针变量。
两种方法都可以使用。
以上简要介绍了go语言中的指针和结构体。
不知道你有没有听过这么一句:在使用 map 时尽量不要在 big map 中保存指针。好吧,你现在已经听过了:)为什么呢?原因在于 Go 语言的垃圾回收器会扫描标记 map 中的所有元素,GC 开销相当大,直接GG。
这两天在《Mastering Go》中看到 GC 这一章节里面对比 map 和 slice 在垃圾回收中的效率对比,书中只给出结论没有说明理由,这我是不能忍的,于是有了这篇学习笔记。扯那么多,Show Your Code
这是一个简单的测试程序,保存字符串的 map 和 保存整形的 map GC 的效率相差几十倍,是不是有同学会说明明保存的是 string 哪有指针?这个要说到 Go 语言中 string 的底层实现了,源码在 src/runtime/string.go里,可以看到 string 其实包含一个指向数据的指针和一个长度字段。注意这里的是否包含指针,包括底层的实现。
Go 语言的 GC 会递归遍历并标记所有可触达的对象,标记完成之后将所有没有引用的对象进行清理。扫描到指针就会往下接着寻找,一直到结束。
Go 语言中 map 是基于 数组和链表 的数据结构实现的,通过 优化的拉链法 解决哈希冲突,每个 bucket 可以保存 8 对键值,在 8 个键值对数据后面有一个 overflow 指针,因为桶中最多只能装 8 个键值对,如果有多余的键值对落到了当前桶,那么就需要再构建一个桶(称为溢出桶),通过 overflow 指针链接起来。
因为 overflow 指针的缘故,所以无论 map 保存的是什么,GC 的时候就会把所有的 bmap 扫描一遍,带来巨大的 GC 开销。官方 issues 就有关于这个问题的讨论, runtime: Large maps cause significant GC pauses #9477
无脑机翻如下:
如果我们有一个map [k] v,其中k和v都不包含指针,并且我们想提高扫描性能,则可以执行以下操作。
将“ allOverflow [] unsafe.Pointer”添加到 hmap 并将所有溢出存储桶存储在其中。 然后将 bmap 标记为noScan。 这将使扫描非常快,因为我们不会扫描任何用户数据。
实际上,它将有些复杂,因为我们需要从allOverflow中删除旧的溢出桶。 而且它还会增加 hmap 的大小,因此也可能需要重新整理数据。
最终官方在 hmap 中增加了 overflow 相关字段完成了上面的优化,这是具体的 commit 地址。
下面看下具体是如何实现的,源码基于 go1.15,src/cmd/compile/internal/gc/reflect.go 中
通过注释可以看出,如果 map 中保存的键值都不包含指针(通过 Haspointers 判断),就使用一个 uintptr 类型代替 bucket 的指针用于溢出桶 overflow 字段,uintptr 类型在 GO 语言中就是个大小可以保存得下指针的整数,不是指针,就相当于实现了 将 bmap 标记为 noScan, GC 的时候就不会遍历完整个 map 了。随着不断的学习,愈发感慨 GO 语言中很多模块设计得太精妙了。
差不多说清楚了,能力有限,有不对的地方欢迎留言讨论,源码位置还是问的群里大佬 _
Go语言里面的指针和C++指针一样,都是指向某块内存的地址值,可以解引用,不同只是在于C++里可以直接对指针做算术运算而Go里面不行。