Go总结(三)| 函数传值和传指针的区别
其实Go语言函数中所有的传参传递的都是拷贝。只不过有的copy是值类型的副本,有些copy是指针类型的副本。
Go语言实战
《Go语言实战》这本书有下面这两段描述。
内置类型是由语言提供的一组类型。我们已经见过这些类型,分别是数值类型、字符串类型和布尔类型。这些类型本质上是原始的类型。因此,当对这些值进行增加或者删除的时候,会创建一个新值。基于这个结论,当把这些类型的值传递给方法或者函数时,应该传递一个对应值的副本。
Go语言里的引用类型有如下几个:切片、映射、通道、接口和函数类型。当声明上述类型的变量时,创建的变量被称作标头(header)值。从技术细节上说,字符串也是一种引用类型。每个引用类型创建的标头值是包含一个指向底层数据结构的指针。每个引用类型还包含一组独特的字段,用于管理底层数据结构。因为标头值是为复制而设计的,所以永远不需要共享一个引用类型的值。标头值里包含一个指针,因此通过复制来传递一个引用类型的值的副本,本质上就是在共享底层数据结构。
我的理解
回顾一下数据类型分类:
- 基础类型:
boolean、number、string
- 聚合类型:
array、struct
- 引用类型:
pointer、slice、map、channel、func
- 接口类型:
interface
这里1,2是值类型,传参的时候真是把值完完全全的复制一份,形参会在当前函数作用域下申请新的内存空间存放传入的参数值,也就是在函数中你对形参变量的修改,改的是函数体中栈内存的数据,不会影响到调用函数的实参变量。
而3,4类型是引用类型,也就是这些类型的变量都不是具体的值,他们只记录具体值对应的内存地址。传参时候你把引用类型的变量传给函数,函数也会在自己的作用域内部建立临时的变量内存空间,同样copy一份实参的值到这些临时变量中;只不过copy过来的值是指向存放真实数据的内存对应的地址。因此实参和形参存放的值是相同的,都是地址值,这个地址对应的内存空间存放着真实数据。
所以说Go语言中函数传参都是拷贝副本,只是副本是值类型或引用类型的区别。
值类型和引用类型占用内存空间的情况,我们已经在上一篇分享过了。通过对所有类型,以及对不同类型占用内存空间的了解,我想理解传值和传引用(指针)应该毫不费力。
string
这里我想对string重点说明一下。
string是基础数据类型,可以看做是值类型,但是底层技术实现却类似引用类型。很绕是吧,怎样理解string类型呢?看下面的示意图:
看明白了吗?
字符串底层技术实现的确是引用,字符串变量占用16个字节,即一个指针指向首字符起始地址,一个整形存放字节长度。和其它引用类型不一样,不允许修改字符串变量指向的内存(用unsafe包中的方法除外)。
可以这么理解字符串,他就像具备两个字段的struct一样,是一个值类型;好比下面的定义:
|
|
字符串变量不赋值就是空字符串;一旦赋值,他就能表示一段字符串值,但是因为你永远无法改变这些值,改变字符串变量相当于存放新的字符串。你不需要知道值存在哪里;你甚至可以当他不存在,如果这么想,字符串变量当做值类型来处理就好理解了。
slice
Golang中的切片类型极富特色,需要好好理解。下面这篇博文介绍的不错。
下面是几个简单的测试代码,不妨参考一下:
|
|
输出结果如下:
|
|
切片详细介绍,请参考:
https://www.jianshu.com/p/354fce23b4f0
map
Golang中map的实现和C++中的实现有区别,C++采用红黑树的方式实现,Golang采用哈希表加链表法解决快速索引和冲突查找。具体可以看看下面文章的分析。
参考:
https://www.jb51.net/article/201285.htm
https://www.jianshu.com/p/5d169f887865
https://my.oschina.net/renhc/blog/2208417
(完)
- 原文作者: 闪电侠
- 原文链接:https://chende.ren/2020/11/26122308-003-pass-value-pointer.html
- 版权声明:本作品采用 开放的「署名 4.0 国际 (CC BY 4.0)」创作共享协议 进行许可