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-bufio 缓冲扫描

    前面两篇博客 介绍了 bufio 包中的缓冲读和写 bufio go 下面再来介绍一下缓冲扫描 scan go 这个扫描的是用来对缓存读的更高级封装 提供了一些更易用的方法 缓冲扫描 Scanner 提供了一个方便的接口来读取数据 例如使用
  • Go的并发的退出

    有时候我们需要通知goroutine停止它正在干的事情 比如一个正在执行计算的web服务 然而它的客户端已经断开了和服务端的连接 Go语言并没有提供在一个goroutine中终止另一个goroutine的方法 由于这样会导致goroutin
  • 七. go 常见数据结构实现原理之 反射

    目录 一 golang 是如何实现反射的 如何比较两个对象完全相等 一 golang 是如何实现反射的 参考博客Go 语言问题集 Go Questions Go 语言在 reflect 包里定义了各种类型 实现了反射的各种函数 通过它们可以
  • golang: Logrus实现日志打印

    Github https github com sirupsen logrus golang标准库的日志框架非常简单 仅仅提供了print panic和fatal三个函数 对于更精细的日志级别 日志文件分割以及日志分发等方面并没有提供支持
  • 带你使用Golang快速构建出命令行应用程序

    在日常开发中 大家对命令行工具 CLI 想必特别熟悉了 如果说你不知道命令工具 那你可能是个假开发 每天都会使用大量的命令行工具 例如最常用的Git Go Docker等 不管是做技术开发还是业务开发 都会有开发命令行程序的场景 例如如果是
  • Qt webengine 显示web页面、前后端通信以及下载详解

    概述 官方文档 https doc qt io archives qt 5 11 qtwebengine overview html 翻译文档 Qt5 9 WebEngine 概述 一花一世界 一叶一乾坤 博客园 从Qt5 5开始 Qt W
  • golang:环境变量GOPROXY和GO111MODULE设置

    我们安装完golang后 我们在windows的cmd命令下就可以直接查看和使用go命令和环境变量了 同样的在linux下可以在控制台使用 如下图所示 C Users lijie1 gt go env set GO111MODULE set
  • Golang连接Jenkins获取Job Build状态及相关信息

    文章目录 1 连接Jenkins 2 controller 3 module 4 router 5 效果展示 第三方包 gojenkins 方法文档 gojenkins docs 实现起来很简单 利用第三方库 连接jenkins 调用相关方
  • go 进阶 gin实战相关: 五. gin_scaffold 企业脚手架

    目录 一 gin scaffold 企业级脚手架 二 gin scaffold 脚手架安装及使用演示 文件分层解释 开始使用 1 配置开启go mod 功能 2 下载 安装 gin scaffold 3 整合 golang common 4
  • Go_接口、多态、接口继承、空接口、类型断言

    接口 接口是把所有具有共性的方法定义在一起 是方法集 任何类型实现了接口中所有的方法 就是实现了这个接口 接口可以实现多态 接口传递的是地址值 接口定义及调用 定义格式 tepe 接口名 interface 方法名 参数 返回值 调用格式1
  • 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 程序编译过程(基于 Go1.21)

    版本说明 Go 1 21 官方文档 Go 语言官方文档详细阐述了 Go 语言编译器的具体执行过程 Go1 21 版本可以看这个 https github com golang go tree release branch go1 21 sr
  • GoLong的学习之路,进阶,微服务之序列化协议,Protocol Buffers V3

    这章是接上一章 使用 RPC包 序列化中没有详细去讲 因为这一块需要看的和学习的地方很多 并且这一块是RPC中可以说是最重要的一块 也是性能的重要影响因子 今天这篇主要会讲其使用方式 文章目录 Protocol Buffers V3 背景以
  • Go 语言中切片的使用和理解

    切片与数组类似 但更强大和灵活 与数组一样 切片也用于在单个变量中存储相同类型的多个值 然而 与数组不同的是 切片的长度可以根据需要增长和缩小 在 Go 中 有几种创建切片的方法 使用 datatype values 格式 从数组创建切片
  • 【go语言】error错误机制及自定义错误返回类型

    简介 Go 语言通过内置的 error 接口来处理错误 该接口定义如下 type error interface Error string 这意味着任何实现了 Error 方法的类型都可以作为错误类型 在 Go 中 通常使用 errors
  • 【go语言】error错误机制及自定义错误返回类型

    简介 Go 语言通过内置的 error 接口来处理错误 该接口定义如下 type error interface Error string 这意味着任何实现了 Error 方法的类型都可以作为错误类型 在 Go 中 通常使用 errors
  • Golang拼接字符串性能对比

    g o l a n g golang g o l an g
  • 这套Go语言开发框架组合真的非常高效

    我尝试过很多框架 从Django Flask和Laravel到NextJS和SvelteKit 到目前为止 这是我唯一可以使用的不会让我感到疯狂或者放弃项目的堆栈 框架 我喜欢所有这些框架 但我只是不太适应它们的设计方式 实际上 我是一个弱
  • go cannot find package “github.com/gorilla/websocket“解读

    Go无法找到包 github com gorilla websocket 的解决方案 在Go开发过程中 我们经常会依赖第三方库来简化开发工作 而使用 go get 命令安装这些库时 有时候我们可能会遇到类似于以下错误的情况 plaintex

随机推荐