[]*T *[]T *[]*T 傻傻分不清楚(上)

简介: 作为一个 Go 语言新手,看到一切”诡异“的代码都会感到好奇;比如我最近看到的几个方法;

前言


作为一个 Go 语言新手,看到一切”诡异“的代码都会感到好奇;比如我最近看到的几个方法;伪代码如下:


func FindA() ([]*T,error) {
}
func FindB() ([]T,error) {
}
func SaveA(data *[]T) error {
}
func SaveB(data *[]*T) error {
}


相信大部分刚入门 Go 的新手看到这样的代码也是一脸懵逼,其中最让人疑惑的就是:


[]*T
*[]T
*[]*T


这样对切片的声明,先不看后面两种写法;单独看 []*T 还是很好理解的: 该切片中存放的是所有 T 的内存地址,会比存放 T 本身来说要更省空间,同时 []*T 在方法内部是可以修改 T 的值,而[]T 是修改不了。


func TestSaveSlice(t *testing.T) {
  a := []T{{Name: "1"}, {Name: "2"}}
  for _, t2 := range a {
    fmt.Println(t2)
  }
  _ = SaveB(a)
  for _, t2 := range a {
    fmt.Println(t2)
  }
}
func SaveB(data []T) error {
  t := data[0]
  t.Name = "1233"
  return nil
}
type T struct {
  Name string
}


比如以上例子打印的是


{1}
{2}
{1}
{2}


只有将方法修改为


func SaveB(data []*T) error {
  t := data[0]
  t.Name = "1233"
  return nil
}


才能修改 T 的值:


&{1}
&{2}
&{1233}
&{2}


示例


下面重点来看看 []*T*[]T 的区别,这里写了两个 append 函数:


func TestAppendA(t *testing.T) {
  x:=[]int{1,2,3}
  appendA(x)
  fmt.Printf("main %v\n", x)
}
func appendA(x []int) {
  x[0]= 100
  fmt.Printf("appendA %v\n", x)
}


先看第一种,输出是结果是:


appendA [1000 2 3]
main [1000 2 3]


说明在函数传递过程中,函数内部的修改能够影响到外部。


下面我们再看一个例子:


func appendB(x []int) {
  x = append(x, 4)
  fmt.Printf("appendA %v\n", x)
}


最终结果却是:


appendA [1 2 3 4]
main [1 2 3]


没有影响到外部。


而当我们再调整一下会发现又有所不同:



最终的结果:


appendA &[1 2 3 4]
main [1 2 3 4]


可以发现如果传递切片的指针时,使用 append 函数追加数据时会影响到外部。


slice 原理


在分析上面三种情况之前,我们先来了解下 slice 的数据结构。


直接查看源码会发现 slice 其实就是一个结构体,只是不能直接对外访问。


网络异常,图片无法展示
|


源码地址 runtime/slice.go


其中有三个重要的属性:


属性 含义
array 底层存放数据的数组,是一个指针。
len 切片长度
cap 切片容量 cap>=len


提到切片就不得不想到数组,可以这么理解:


切片是对数组的抽象,而数组则是切片的底层实现。


其实通过切片这个名字也不难看出,它就是从数组中切了一部分;相对于数组的固定大小,切片可以根据实际使用情况进行扩容。


所以切片也可以通过对数组"切一刀"获得:


x1:=[6]int{0,1,2,3,4,5}
x2 := x[1:4]
fmt.Println(len(x2), cap(x2))


网络异常,图片无法展示
|


其中 x1 的长度与容量都是6。


x2 的长度与容量则为3和5。


  • x2 的长度很容易理解。


  • 容量等于5可以理解为,当前这个切片最多可以使用的长度。


因为切片 x2 是对数组 x1 的引用,所以底层数组排除掉左边一个没有被引用的位置则是该切片最大的容量,也就是5。


同一个底层数组


以刚才的代码为例:


func TestAppendA(t *testing.T) {
  x:=[]int{1,2,3}
  appendA(x)
  fmt.Printf("main %v\n", x)
}
func appendA(x []int) {
  x[0]= 100
  fmt.Printf("appendA %v\n", x)
}


网络异常,图片无法展示
|


在函数传递过程中,main 中的 x 与 appendA 函数中的 x 切片所引用的是同个数组。

所以在函数中对 x[0]=100main函数中也能获取到。


网络异常,图片无法展示
|


本质上修改的就是同一块内存数据。



相关文章
|
4天前
|
云安全 人工智能 安全
AI被攻击怎么办?
阿里云提供 AI 全栈安全能力,其中对网络攻击的主动识别、智能阻断与快速响应构成其核心防线,依托原生安全防护为客户筑牢免疫屏障。
|
14天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
8天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
567 211
|
4天前
|
编解码 Linux 数据安全/隐私保护
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
228 138
|
存储 人工智能 监控
从代码生成到自主决策:打造一个Coding驱动的“自我编程”Agent
本文介绍了一种基于LLM的“自我编程”Agent系统,通过代码驱动实现复杂逻辑。该Agent以Python为执行引擎,结合Py4j实现Java与Python交互,支持多工具调用、记忆分层与上下文工程,具备感知、认知、表达、自我评估等能力模块,目标是打造可进化的“1.5线”智能助手。
800 59
|
6天前
|
人工智能 移动开发 自然语言处理
2025最新HTML静态网页制作工具推荐:10款免费在线生成器小白也能5分钟上手
晓猛团队精选2025年10款真正免费、无需编程的在线HTML建站工具,涵盖AI生成、拖拽编辑、设计稿转代码等多种类型,均支持浏览器直接使用、快速出图与文件导出,特别适合零基础用户快速搭建个人网站、落地页或企业官网。
1118 157
|
6天前
|
存储 安全 固态存储
四款WIN PE工具,都可以实现U盘安装教程
Windows PE是基于NT内核的轻量系统,用于系统安装、分区管理及故障修复。本文推荐多款PE制作工具,支持U盘启动,兼容UEFI/Legacy模式,具备备份还原、驱动识别等功能,操作简便,适合新旧电脑维护使用。
479 109