golang slice 是 struct 类型,作为参数传递时要传地址

2023-11-14

总结:slice 为结构体类型,go 传参都是值传递,跨函数传递 slice 时,要传变量的地址,不能直接传递变量值,因为除了 len 会变,遇到扩容时,还会新建底层数组。

先看下 slice 的定义

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

slice 是一个 struct,包含 array,len,cap 三个属性,其中 array 为 unsafe.Pointer 类型,且是 struct 的第一个属性,所以 array 的地址即为该 struct 的地址

dat := make([]int, 6)

上面的代码定义了一个 []int 类型的变量 dat,dat 是一个引用类型,指向底层数组的地址,其实我们要把 dat 看成一个 struct 类型,因为 len 和 cap 能影响 dat,看下面的代码

import (
	"fmt"
	"testing"
)

func fnA() {
	dat0 := make([]int,6, 10)
	dat := dat0[0:5]
	dat[0] = 111
	fmt.Printf("at fnA before fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
	fmt.Printf("at fnA before fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
	fmt.Printf("at fnA before fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
	fnB(dat)
	fmt.Printf("at fnA after fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
	fmt.Printf("at fnA after fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
	fmt.Printf("at fnA after fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
}

func fnB(a []int) {
	fmt.Printf("at fnb before update a : %v, len(a) = %d \n", a, len(a))
	a[0]=200
	fmt.Printf("at fnb after update a : %v, len(a) = %d \n", a, len(a))
	fmt.Printf("at fnb before append a addr is %p, var addr %p \n", a, &a)
	a = append(a, 120)
	fmt.Printf("at fnb after append a addr is %p, var addr %p \n", a, &a)
	fmt.Printf("at fnb after append a : %v, len(a) = %d \n", a, len(a))
}

func TestPermute(t *testing.T){
	fnA()
}
=== RUN   TestPermute
at fnA before fnB(dat) dat value is [111 0 0 0 0], len(dat) = 5 
at fnA before fnB(dat) dat0 value is [111 0 0 0 0 0], len(dat) = 6 
at fnA before fnB(dat) dat addr is 0xc0000220a0, var addr 0xc00000c0c0 
at fnb before update a : [111 0 0 0 0], len(a) = 5 
at fnb after update a : [200 0 0 0 0], len(a) = 5 
at fnb before append a addr is 0xc0000220a0, var addr 0xc00000c140 
at fnb after append a addr is 0xc0000220a0, var addr 0xc00000c140 
at fnb after append a : [200 0 0 0 0 120], len(a) = 6 
at fnA after fnB(dat) dat addr is 0xc0000220a0, var addr 0xc00000c0c0 
at fnA after fnB(dat) dat value is [200 0 0 0 0], len(dat) = 5 
at fnA after fnB(dat) dat0 value is [200 0 0 0 0 120], len(dat) = 6 
--- PASS: TestPermute (0.00s)
PASS

由于 golang 是值传递,所以 fnB(dat) 会创建一个新的 struct,这个可以从 &dat 与 &a 的值不同,以及 a 的 len 也没有传递给 dat 看出来。如果把 dat0 := make([]int,6, 10) 改为 dat0 := make([]int,0),可以看出底层数组也会改变

import (
	"fmt"
	"testing"
)

func fnA() {
	//dat0 := make([]int,6, 10)
	//dat0 := make([]int,0)
	//dat := dat0[0:5]
	dat := make([]int,0)
	//dat[0] = 111
	fmt.Printf("at fnA before fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
	//fmt.Printf("at fnA before fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
	fmt.Printf("at fnA before fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
	fnB(dat)
	fmt.Printf("at fnA after fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
	fmt.Printf("at fnA after fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
	//fmt.Printf("at fnA after fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
}

func fnB(a []int) {
	fmt.Printf("at fnb before update a : %v, len(a) = %d \n", a, len(a))
	//a[0]=200
	fmt.Printf("at fnb after update a : %v, len(a) = %d \n", a, len(a))
	fmt.Printf("at fnb before append a addr is %p, var addr %p \n", a, &a)
	a = append(a, 120)
	fmt.Printf("at fnb after append a addr is %p, var addr %p \n", a, &a)
	fmt.Printf("at fnb after append a : %v, len(a) = %d \n", a, len(a))
}

func TestPermute(t *testing.T){
	fnA()
}
=== RUN   TestPermute
at fnA before fnB(dat) dat value is [], len(dat) = 0 
at fnA before fnB(dat) dat addr is 0x6520a8, var addr 0xc00000c0c0 
at fnb before update a : [], len(a) = 0 
at fnb after update a : [], len(a) = 0 
at fnb before append a addr is 0x6520a8, var addr 0xc00000c120 
at fnb after append a addr is 0xc0000202e8, var addr 0xc00000c120 
at fnb after append a : [120], len(a) = 1 
at fnA after fnB(dat) dat addr is 0x6520a8, var addr 0xc00000c0c0 
at fnA after fnB(dat) dat value is [], len(dat) = 0 
--- PASS: TestPermute (0.00s)
PASS

Process finished with exit code 0

换成传地址就能返回想要的结果

import (
	"fmt"
	"testing"
)

func fnA() {
	//dat0 := make([]int,6, 10)
	//dat0 := make([]int,0)
	//dat := dat0[0:5]
	dat := make([]int,0)
	//dat[0] = 111
	fmt.Printf("at fnA before fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
	//fmt.Printf("at fnA before fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
	fmt.Printf("at fnA before fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
	fnB(&dat)
	fmt.Printf("at fnA after fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
	fmt.Printf("at fnA after fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
	//fmt.Printf("at fnA after fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
}

func fnB(a *[]int) {
	fmt.Printf("at fnb before update a : %v, len(a) = %d \n", *a, len(*a))
	//a[0]=200
	fmt.Printf("at fnb after update a : %v, len(a) = %d \n", *a, len(*a))
	//fmt.Printf("at fnb before append a addr is %p, var addr %p \n", a, &a)
	fmt.Printf("at fnb before append a addr is %p, var addr %p \n", *a, &a)
	*a = append(*a, 120)
	//fmt.Printf("at fnb after append a addr is %p, var addr %p \n", a, &a)
	fmt.Printf("at fnb after append a addr is %p, var addr %p \n", *a, &a)
	fmt.Printf("at fnb after append a : %v, len(a) = %d \n", *a, len(*a))
}

func TestPermute(t *testing.T){
	fnA()
}
=== RUN   TestPermute
at fnA before fnB(dat) dat value is [], len(dat) = 0 
at fnA before fnB(dat) dat addr is 0x6520a8, var addr 0xc00000c0c0 
at fnb before update a : [], len(a) = 0 
at fnb after update a : [], len(a) = 0 
at fnb before append a addr is 0x6520a8, var addr 0xc00000e038 
at fnb after append a addr is 0xc0000202e8, var addr 0xc00000e038 
at fnb after append a : [120], len(a) = 1 
at fnA after fnB(dat) dat addr is 0xc0000202e8, var addr 0xc00000c0c0 
at fnA after fnB(dat) dat value is [120], len(dat) = 1 
--- PASS: TestPermute (0.00s)
PASS

Process finished with exit code 0

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

golang slice 是 struct 类型,作为参数传递时要传地址 的相关文章

  • Golang适合高并发场景的原因分析

    典型的两个现实案例 我们先看两个用Go做消息推送的案例实际处理能力 360消息推送的数据 16台机器 标配 24个硬件线程 64GB内存 Linux Kernel 2 6 32 x86 64 单机80万并发连接 load 0 2 0 4 C
  • Go语言实现区块链与加密货币-Part3(交易优化,单机模拟多节点通信)

    交易 二 在这个系列文章的一开始 我们就提到了 区块链是一个分布式数据库 不过在之前的文章中 我们选择性地跳过了 分布式 这个部分 而是将注意力都放到了 数据库 部分 到目前为止 我们几乎已经实现了一个区块链数据库的所有元素 今天 我们将会
  • go Cobra命令行工具入门

    简介 Github https github com spf13 cobra Star 26 5K Cobra是一个用Go语言实现的命令行工具 并且现在正在被很多项目使用 例如 Kubernetes Hugo和Github CLI等 通过使
  • Golang-使用 goroutine 运行闭包的“坑”

    介绍 在 Go 语言中 函数支持匿名函数 闭包就是一种特殊的匿名函数 它可以用于访问函数体外部的变量 需要注意的是 在 for range 中 使用 goroutine 执行闭包时 经常会掉 坑 因为匿名函数可以访问函数体外部的变量 而 f
  • go语言基础-----03-----流程控制、函数、值传递、引用传递、defer函数

    1 流程控制 这里只讲 for range 语句 这个关键字 主要用于遍历 用来遍历数组 slice map chan 例如 package main import fmt func main str hello world 中国 for
  • Golang连接Jenkins获取Job Build状态及相关信息

    文章目录 1 连接Jenkins 2 controller 3 module 4 router 5 效果展示 第三方包 gojenkins 方法文档 gojenkins docs 实现起来很简单 利用第三方库 连接jenkins 调用相关方
  • Golang协程与通道整理

    协程goroutine 不由OS调度 而是用户层自行释放CPU 从而在执行体之间切换 Go在底层进行协助实现 涉及系统调用的地方由Go标准库协助释放CPU 总之 不通过OS进行切换 自行切换 系统运行开支大大降低 通道channel 并发编
  • goland环境配置

    goland modules环境配置 下载和安装goland 环境配置 配置环境变量GOPATH 配置go modules GOPROXY代理的系统变量 工程目录中新建三个工作目录 goland中启用go modules 新建一个go程序
  • Go 语言输出文本函数详解

    Go语言拥有三个用于输出文本的函数 Print Println Printf Print 函数以其默认格式打印其参数 示例 打印 i 和 j 的值 package main import fmt func main var i j stri
  • 基于Go语言实现简易Web应用

    目录 前言 Go语言特点 写在使用Go语言实现Web应用前面 创建Web服务器 声明一个结构体操作 加入中间件的使用 使用静态文件服务器 最后 前言 在编程语言中 近几年问世的几个新语言都是非常不错的 比如Go Python Rust等等
  • 为什么最近听说 Go 岗位很少很难?

    大家好 我是煎鱼 其实这个话题已经躺在我的 TODO 里很久了 近来很多社区的小伙伴都私下来交流 也有在朋友圈看到朋友吐槽 Go 上海的大会没什么人 还不如 Rust 大会 比较尴尬 今天主要是看看为什么 Go 岗位看起来近来很难的样子 也
  • 【go语言开发】编写单元测试

    本文主要介绍使用go语言编写单元测试用例 首先介绍如何编写单元测试 然后介绍基本命令的使用 最后给出demo示例 文章目录 前言 命令 示例 前言 在go语言中编写单元测试时 使用说明 测试文件命名 在 Go 语言中 测试文件的命名应与被测
  • go-zero 开发入门-加法客服端示例

    定义 RPC 接口文件 接口文件 add proto 的内容如下 syntax proto3 package add 当 protoc gen go 版本大于 1 4 0 时需加上 go package 否则编译报错 unable to d
  • GoLong的学习之路,进阶,微服务之序列化协议,Protocol Buffers V3

    这章是接上一章 使用 RPC包 序列化中没有详细去讲 因为这一块需要看的和学习的地方很多 并且这一块是RPC中可以说是最重要的一块 也是性能的重要影响因子 今天这篇主要会讲其使用方式 文章目录 Protocol Buffers V3 背景以
  • go语言实现文件夹上传前后端代码案例

    go语言实现文件夹上传前后端代码案例 前端用于上传的测试界面 如果上传的文件夹有子文件要遍历子文件夹创建出子文件夹再进行拷贝 需要获取文件名和对应的路径 将文件的相对路径和文件对象添加到FormData中 这几行代码很关键 for let
  • go开发--操作mysql数据库

    在 Go 中访问 MySQL 数据库并进行读写操作通常需要使用第三方的 MySQL 驱动 Go 中常用的 MySQL 驱动有 github com go sql driver mysql 和 github com go xorm xorm
  • Golang拼接字符串性能对比

    g o l a n g golang g o l an g
  • Golang拼接字符串性能对比

    g o l a n g golang g o l an g
  • go-carbon v2.3.4 发布,轻量级、语义化、对开发者友好的 Golang 时间处理库

    carbon 是一个轻量级 语义化 对开发者友好的 golang 时间处理库 支持链式调用 目前已被 awesome go 收录 如果您觉得不错 请给个 star 吧 github com golang module carbon gite
  • 【go语言】结构体数据填充生成md错误码文件

    这里使用pongo2这个模版引擎库进行md文件渲染 GitHub flosch pongo2 Django syntax like template engine for Go package main import fmt github

随机推荐