从一道题来看看golang中的slice作为参数时的现象
1、题目
最近看群友在群里问一道关于golang中slice的题,题目如下:
package main
import "fmt"
func main() {
k := []int{1, 2, 3, 4}
k = append(k, 5, 6)
fmt.Printf("k --> value: %v, add: %p, cap: %d\n", k, k, cap(k))
ap(k)
fmt.Printf("k --> value: %v, add: %p, cap: %d\n", k, k, cap(k))
}
func ap(k []int) {
k = append(k, 7, 8)
fmt.Printf("k --> value: %v, add: %p, cap: %d\n", k, k, cap(k))
}
执行结果:
k --> value: [1 2 3 4 5 6], add: 0xc00001e180, cap: 8
k --> value: [1 2 3 4 5 6 7 8], add: 0xc00001e180, cap: 8
k --> value: [1 2 3 4 5 6], add: 0xc00001e180, cap: 8
乍一看,还挺奇怪的,变量k的地址都是一样的,为啥会执行ap函数时,打印出来的东西不一样呢?
其实对于初次接触 golang 的 gopher 而言,这个问题确实有点奇怪,书上不是说slice是引用类型,golang 中的函数传参是值拷贝,那么在函数传递 slice 时,传递也是地址,为啥对地址指向的内容做了修改后,并没有影响到其他指向同一地址的变量呢?
想要理解这里面的原理,需要了解下面的基础知识,接下来我们先看看前置知识,学习完这些前置的理论后,相信大家都已经有了自己的理解与答案。
PS: 要是有理解不对的地方,请不吝赐教哈,谢谢。
2、前置理论
2.1、切片的本质
下面的介绍基于 go 1.18,golang中关于 slice 封装的源码位于
runtime/slice.go中。
切片的本质就是对底层数组的封装,切片实际上是一个 struct ,包含了三个字段:底层数组的指针、切片的长度(len)和切片的容量(cap)。
type slice struct {
array unsafe.Pointer // 数组指针
len int // 长度
cap int // 容量
}
slice 作为参数传递的时候,是将slice struct中的各个字段逐一复制到新的变量中去的,其中 array 字段是底层数组的首地址。
我们一起来看看题目中变量K的初始化
k := []int{1, 2, 3, 4}
k = append(k, 5, 6)
变量 K 示意图:

执行 ap 函数后
func ap(k []int) {
k = append(k, 7, 8) // 无需扩容,容量足够
fmt.Printf("k --> value: %v, add: %p, cap: %d\n", k, k, cap(k))
}
函数内变量k的示意图:

2.2、格式化字符串%p打印slice时显示的是什么
这个问题呢,推荐大家看下这篇文章,比我说得清楚写。
[golang slice切片到底是指针吗?为什么%p输出的切片是地址?](https://segmentfault.com/a/1190000042430248)
这里我们写一个demo验证下
func main() {
k := []int{1, 2, 3, 4}
fmt.Printf("k --> add: %p\n", k)
fmt.Printf("k[0] --> add: %p\n", &k[0])
}
执行结果:
k --> add: 0xc000136000
k[0] --> add: 0xc000136000
3、再看题目
了解了上面的知识后,再看开头的题目就很简单了,变量k 传给 ap 函数函数时,虽然函数 ap 的形参也叫 k,但是已经不是同一个变量了,只是两个 slice 指向的底层数组是同一个而已,所以使用 %p 打印时,显示的地址是一样的。
package main
import "fmt"
func main() {
k := []int{1, 2, 3, 4}
k = append(k, 5, 6)
fmt.Printf("k --> value: %v, add: %p, len: %d, cap: %d\n", k, k, len(k), cap(k))
fmt.Printf("k --> add: %p\n", &k)
ap(k)
fmt.Printf("k --> value: %v, add: %p, len: %d, cap: %d\n", k, k, len(k), cap(k))
fmt.Printf("k --> add: %p\n", &k)
}
func ap(k []int) {
k = append(k, 7, 8)
fmt.Printf("k --> value: %v, add: %p, len: %d, cap: %d\n", k, k, len(k), cap(k))
fmt.Printf("k --> add: %p\n", &k)
}
执行结果:
k --> value: [1 2 3 4 5 6], add: 0xc00001e180, len: 6, cap: 8
k --> add: 0xc00000c030
k --> value: [1 2 3 4 5 6 7 8], add: 0xc00001e180, len: 8, cap: 8
k --> add: 0xc00000c078
k --> value: [1 2 3 4 5 6], add: 0xc00001e180, len: 6, cap: 8
k --> add: 0xc00000c030
想要 ap 函数执行后的结果,能够改变外面的变量k也很简单,将函数中的形参k返回出去就可以了。类似这样:
func ap(k []int) []int {
k = append(k, 7, 8)
return k
}
k = ap(k)
是不是有点像 append 内置函数
从一道题来看看golang中的slice作为参数时的现象的更多相关文章
- golang中,slice的几个易混淆点
slice在golang中是最常用的类型,一般可以把它作为数组使用,但是比数组要高效呀.不过,我感觉这个东西用的不好坑太多了.还是需要了解下他底层的实现 slice的结构定义 type slice s ...
- Golang中设置函数默认参数的优雅实现
在Golang中,我们经常碰到要设置一个函数的默认值,或者说我定义了参数值,但是又不想传递值,这个在python或php一类的语言中很好实现,但Golang中好像这种方法又不行.今天在看Grpc源码时 ...
- Golang中的Slice与数组
1.Golang中的数组 数组是一种具有固定长度的基本数据结构,在golang中与C语言一样数组一旦创建了它的长度就不允许改变,数组的空余位置用0填补,不允许数组越界. 数组的一些基本操作: 1.创建 ...
- 【Java学习笔记之二十七】Java8中传多个参数时的方法
java中传参数时,在类型后面跟"..."的使用: public static void main(String[] args){ testStringA ...
- golang中的slice翻转存在以及map中的key判断
//slice翻转 func stringReverse(src []string){ if src == nil { panic(fmt.Errorf("the src can't be ...
- Go_17:GoLang中如何使用多参数属性传参
我们常常因为传入的参数不确定而头疼不已,golang 为我们提供了接入多值参数用于解决这个问题.但是一般我们直接写已知代码即所有的值都知道一个一个塞进去就好了,但是绝大部分我们是得到用户的大量输入想通 ...
- GoLang中如何使用多参数属性传参
我们常常因为传入的参数不确定而头疼不已,golang 为我们提供了接入多值参数用于解决这个问题.但是一般我们直接写已知代码即所有的值都知道一个一个塞进去就好了,但是绝大部分我们是得到用户的大量输入想通 ...
- golang中函数的可变参数
package main import "fmt" // 一个函数中最多只可有一个可变参数, 如果参数列表中还有其它类型的参数,则可变参数写在最后 // 注意:参数不定,参数的个数 ...
- golang中往脚本传递参数的两种用法os.Args和flag
1. os.Args package main import ( "fmt" "os" ) func main() { // 执行:./demo.exe 127 ...
- OC中block作方法参数时的用法
方式一.在传参时直接声明block回调方法. 1. 定义方法: - (int)doTest:(NSString *)name para1:(int)temp1 para2:(int)temp2 suc ...
随机推荐
- 图形视图体系结构——Graphics View
Graphics View框架结构的特点.主要包含元素及坐标系统. 1.特点 Graphics View框架结构的主要特点如下. (1) Graphics View框架结构中,系统可以利用Qt绘图系统 ...
- Linux学习环境搭建(VMware虚拟机安装Linux)
企业现状 目前绝大多数企业运维人员的工作环境都是Windows下通过SSH工具(如XShell等)远程连接千百里外的服务器进行管理和维护的. 而且学Linux运维,99.9%知识与硬件无关,用虚拟机足 ...
- 最全linux基础知识
linux基础知识 [root@localhost ~]# 各位置表示什么意识 root:表示用户名 (现在的用户是root切换为test便是张三) localhost:表示主机名 (当前主机名切换为 ...
- ETL之apache hop系列2-hop web安装和入门
前言 在Docker安装apache hop 首先确保Docker已经安装和运行Java 11 JDK 安装文档参考:https://blog.csdn.net/Chia_Hung_Yeh/artic ...
- AI绘画Stable Diffusion实战操作: 62个咒语调教-时尚杂志封面
今天来给大家分享,如何用sd简单的咒语输出好看的图片的教程,今天做的是时尚杂志专题,话不多说直入主题. 还不会StableDiffusion的基本操作,推荐看看这篇保姆级教程: AI绘画:Stable ...
- Vue【原创】时间轴 【time-axis】&【date-axis】
封装了关于时间轴的组件,有时候统计页面会用到. 效果图: 时间轴分为2种,一种是time-axis:范围选择模式,一种是date-axis:步长选择模式. 代码中涉及到的工具类和图片资源,请移步页面底 ...
- mybatis数据库字段自动填充
背景描述 目前,大多数项目的数据库设计,都会添加一些公共字段,比如version(版本号).deleted(逻辑删除标识).create_time.update_time.create_by.upda ...
- [Python3] 初识py, 一个简单练手的小玩意. 快递查询
有图有真相 脚本代码 最近刚入门py, 准备写点小玩意练练手. 于是决定拿快递100开刀. 因为它的api很简单. # 快递100 API # 作者: 剑齿虎 # 邮箱: yuxiaobo64@gma ...
- 搭一下 Stable Diffusion WebUI
Preface 前不久看到好多朋友用上Stable Diffusion来做原画,然后又配合上了Chatgpt. 一直以来都想尝试一下,奈何2014款的双核mac跑个idea都发出了拖拉机的轰鸣声. 所 ...
- Note -「SOS DP」高维前缀和
本文差不多算是翻译了一遍 CF blog?id=45223 就是抄了一遍,看不懂可以去原文. 当然我的翻译并不是完全遵从原文的. Part. 1 Introduction 平时我们怎么求高维前缀和?容 ...