golang随笔之slice+append的陷阱

前言

有关slice底层点击下面博文阅读
Golang底层原理剖析之slice类型与扩容机制

问题初探

package main

import "fmt"

func main() {
	s1 := []int{1, 2}
	s2 := s1
	s2 = append(s2, 3)
	Test1(s1)
	Test1(s2)
	fmt.Println(s1, s2)
}

func Test1(s []int) {
	s = append(s, 0)
	for i := range s {
		s[i]++
	}
}

A:[2,3] [2,3,4]
B:[1,2] [1,2,3]
C:[1,2] [2,3,4]
D:[2,3,1] [2,3,4,1]

思考

今天看书看到这一题,一看,这肯定选D嘛,一翻答案,选C,离谱!!

我知道s2 = append(s2, 3)这一句话会因为容量不够而发送扩容导致s1和s2的底层不一样,那传入Test1的时候,不是说好slice传递是引用吗,那么s = append(s, 0)扩容换底层array后,s1和Test1的s,他们是引用关系,那他们还是相同的一个slice吗?结果是,如果发生扩容了,那么就相当于值传递,那这样解释的话,s1是能说得通,那s2呢,s2在Test1中并没有发生扩容,为什么s2不是2,3,4,1?来看下面这个例子

package main

import "fmt"

func main() {
	s3:=make([]int,2,10)
	fmt.Println(s3)
	Test2(s3)
	fmt.Println(s3)

	s4:=s3[0:10]
	fmt.Println(s4)
}
func Test2(s []int){
	s=append(s,6)
	s=append(s,6)
	s=append(s,6)
	fmt.Println(s)
}
[0 0]
[0 0 6 6 6]
[0 0]
[0 0 6 6 6 0 0 0 0 0]

我们发现在没有发生扩容的情况下,s3的底层数组,和函数内的s一样,那么为什么s3没有变成006666呢?

解析

将一个切片作为函数参数传递给函数时,其实采用的是值传递,因此传递给函数的参数其实是切片结构体的值拷贝

type SliceHeader struct {
    Data uintptr
    Len int
    Cap int
}

我们以值传递的方式传递切片结构体的时候,同时也是传递了Len和Cap的值拷贝,因为这两个成员并不是指针,因此,当函数返回时,原切片结构体的Len和Cap并没有改变。

那如果想在函数中用append怎么办,这个时候就要传slice的指针了,如下面代码

package main

import "fmt"

func main() {
	arr := []int{1, 2, 3, 4}
	fmt.Println(arr)

	Test1(&arr)

	fmt.Println(arr)
}

func Test1(s *[]int) {
	*s = append(*s, 5)
	fmt.Println(*s)
}
[1 2 3 4]
[1 2 3 4 5]
[1 2 3 4 5]

Go中没有引用传递

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.
文档地址:https://golang.org/ref/spec#Calls

不能单纯因为函数内部的修改可以反馈到外面就认为是传递的引用。

我说怎么有的书上写slice map func的引用转递,有的地方说go都是值传递,看来还是没有说的透彻。

记住,go中全部都是值传递。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cheems~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值