[]*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函数中也能获取到。


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


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



相关文章
|
2月前
|
数据采集 大数据 Python
学Python静不下来,看了一堆资料还是很迷茫是为什么
学Python静不下来,看了一堆资料还是很迷茫是为什么
30 2
学Python静不下来,看了一堆资料还是很迷茫是为什么
|
7月前
|
存储 Java
i++和++i傻傻分不清楚?这里给你最清楚的解答
i++和++i傻傻分不清楚?这里给你最清楚的解答
i++和++i傻傻分不清楚?这里给你最清楚的解答
|
7月前
|
设计模式 网络协议 算法
|
11月前
|
Python
一日一技:你的代码是如何被炫技毁掉的
一日一技:你的代码是如何被炫技毁掉的
76 0
|
机器学习/深度学习 人工智能 Java
|
Java 关系型数据库 MySQL
【浅尝高并发编程】接私活差点翻车
作为一名本本分分的练习时长两年半的Java练习生,一直深耕在业务逻辑里,对并发编程的了解仅仅停留在八股文里。一次偶然的机会,接到一个私活,核心逻辑是写一个 定时访问api把数据持久化到数据库的小服务。
144 0
|
消息中间件 Kubernetes Cloud Native
记一次内部分享——瞎扯淡
记一次内部分享——瞎扯淡
记一次内部分享——瞎扯淡
[]*T *[]T *[]*T 傻傻分不清楚(下)
作为一个 Go 语言新手,看到一切”诡异“的代码都会感到好奇;比如我最近看到的几个方法;
小说系统源码,那些傻傻分不清楚的技术概念
小说系统源码,那些傻傻分不清楚的技术概念
J3
|
机器学习/深度学习 存储 缓存
有图有真相的Java内存模型基础,你好意思不看嘛!
有图有真相的Java内存模型基础
J3
127 0
有图有真相的Java内存模型基础,你好意思不看嘛!

相关实验场景

更多