空切片问题

sharkLoc · 2022-01-13 15:10:43 · 1323 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2022-01-13 15:10:43 的主题,其中的信息可能已经有所发展或是发生改变。

我又来了,代码如下:

s1和s2 都是空切片,长度都为零,为何跟nil比较结果不一样呢?

package main

import "fmt"

func main() {
    var s1 = []string{}
    var s2 []string
    fmt.Println(s1 == nil, s1, len(s1)) // 输出 false [] 0
    fmt.Println(s2 == nil, s2, len(s2)) // 输出 true [] 0
}

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1323 次点击  
加入收藏 微博
18 回复  |  直到 2022-01-24 15:58:11
sharkLoc
sharkLoc · #1 · 3年之前

明白了,s1相当于已经做一次初始化,已经分配内存了,就不等于nil ss.png

liangmanlin
liangmanlin · #2 · 3年之前

这种用法是不对的,不应该对slice进行nil判断,应该进行len()取长度

sharkLoc
sharkLoc · #3 · 3年之前
liangmanlinliangmanlin #2 回复

这种用法是不对的,不应该对slice进行nil判断,应该进行len()取长度

切片本质不是引用类型么,为何不能和nil比较?

liangmanlin
liangmanlin · #4 · 3年之前
sharkLocsharkLoc #3 回复

#2楼 @liangmanlin 切片本质不是引用类型么,为何不能和nil比较?

谁告诉你是引用,你先看看slice的数据结构,而且nil并不是你想象的nil,这里的nil是零值,并不是空

sharkLoc
sharkLoc · #5 · 3年之前
liangmanlinliangmanlin #4 回复

#3楼 @sharkLoc 谁告诉你是引用,你先看看slice的数据结构,而且nil并不是你想象的nil,这里的nil是零值,并不是空

你好,我还是不太理解。

就像下面这个例子,操作函数没有返回值,直接对切片进行操作,这个不是引用类型?

package main

import "fmt"

func mytest(x []int) {
    x[0] = 100
}

func main() {
    var demo []int = []int{1, 2, 3}
    fmt.Println(demo)
    mytest(demo)
    fmt.Println(demo)
}
liangmanlin
liangmanlin · #6 · 3年之前

5楼 @sharkLoc

package main

import "fmt"

func mytest(x []int) {
    x = append(x,1)
}

func main() {
    var demo []int = []int{1, 2, 3}
    fmt.Println(demo)
    mytest(demo)
    fmt.Println(demo)
}

这个代码应该可以足够说明

sharkLoc
sharkLoc · #7 · 3年之前

6楼 @liangmanlin 回六楼:demo前后内容不变,参考 https://golang.google.cn/blog/slices-intro 切片是引用类型。

liangmanlin
liangmanlin · #8 · 3年之前
sharkLocsharkLoc #7 回复

6楼 @liangmanlin 回六楼:demo前后内容不变,参考 https://golang.google.cn/blog/slices-intro 切片是引用类型。

所以你还不理解?如果是引用,为什么不变,因为slice不是引用,他是一个结构体

houyanzu
houyanzu · #9 · 3年之前
liangmanlinliangmanlin #8 回复

#7楼 @sharkLoc 所以你还不理解?如果是引用,为什么不变,因为slice不是引用,他是一个结构体

兄弟,你还是停留在表面啊,试试下边的代码

package main

import "fmt"

func mytest(x []int) {
    x[3] = 4
}

func main() {
    demo := []int{1, 2, 3, 0}
    fmt.Println(demo)
    mytest(demo)
    fmt.Println(demo)
}
sunjiangmeng
sunjiangmeng · #10 · 3年之前

别人说的没错是传递的结构体。去golang源码可以找到slice的源码

type slice struct {
  array unsafe.Pointer  // 数据部分
  len   int          // 长度
  cap   int             // 容量
}

所以len(切片),cap(切片)可以获取到长度。

结构体中的unsafe.Pointer是指向底层数组的真实地址。 你的mytest方法传递就是结构体副本。不过因为他们各自的slice切片结构体的地址值一样,所以操作的数组能够更改。

修改下你的方法,就会说明go切片的传递就是拷贝的结构体

package main

import "fmt"

func mytest(x []int) {
    x =append(x ,0)
    x[3] = 4
    fmt.Println("mytest中x的切片",x)
}

func main() {
    demo := []int{1, 2, 3, 0}
    fmt.Println(demo)
    mytest(demo)
    fmt.Println(demo)
}

运行结果如下

[1 2 3 0]
mytest中x的切片 [1 2 3 4 0]
[1 2 3 0]

之所以demo的切片不是 [1 2 3 4 0],是因为mytest的append发生了扩容,导致x切片中的unsafe.Pointer发生改变了。但这并不会影响到demo切片。因为这两个切片结构体根本不是一个对象

robertchen
robertchen · #11 · 3年之前
houyanzuhouyanzu #9 回复

#8楼 @liangmanlin 兄弟,你还是停留在表面啊,试试下边的代码 ``` package main import "fmt" func mytest(x []int) { x[3] = 4 } func main() { demo := []int{1, 2, 3, 0} fmt.Println(demo) mytest(demo) fmt.Println(demo) } ```

至少, golang 没有c++概念的引用。因为传递slice值仅仅是复制slice的结构体。虽然底层的数组是一个。但是slice却不是一个。 golang不存在共用一个内存地址的不同变量。因此golang没有引用。

liangmanlin
liangmanlin · #12 · 3年之前
houyanzuhouyanzu #9 回复

#8楼 @liangmanlin 兄弟,你还是停留在表面啊,试试下边的代码 ``` package main import "fmt" func mytest(x []int) { x[3] = 4 } func main() { demo := []int{1, 2, 3, 0} fmt.Println(demo) mytest(demo) fmt.Println(demo) } ```

老哥,是你停留在表面。。。。

GO_go_GO1
GO_go_GO1 · #13 · 3年之前
liangmanlinliangmanlin #6 回复

5楼 @sharkLoc ```golang package main import "fmt" func mytest(x []int) { x = append(x,1) } func main() { var demo []int = []int{1, 2, 3} fmt.Println(demo) mytest(demo) fmt.Println(demo) } ``` 这个代码应该可以足够说明

说明啥?先弄明白append 再说

houyanzu
houyanzu · #14 · 3年之前
liangmanlinliangmanlin #12 回复

#9楼 @houyanzu 老哥,是你停留在表面。。。。

没错,切片底层确实是一个结构体,但是这个结构体可以理解为底层数组的引用。此引用当然跟C++的引用不是一样的概念,你可以搜一下goalng的引用类型,开发者已经约定成俗的认为slice、map和channel为引用类型了。你上边说slice不能与nil做比较是不对的,因为slice只有在new或者make之后才会分配内存,只是var s []int,这样的话,s确实是nil。还有你贴出来的代码,想证明slice不是引用,也是不对的,结果不变只是因为底层数组扩容导致了指针改变,而不能说明slice不是引用。

houyanzu
houyanzu · #15 · 3年之前
liangmanlinliangmanlin #12 回复

#9楼 @houyanzu 老哥,是你停留在表面。。。。

附加一句,nil并不是空指针,是goalng特有的类型,并且这里的引用类型也不能理解为C++的引用。

jsonlws
jsonlws · #16 · 3年之前

这个其实很好理解,首先是s1这个是相当于赋值了,等于make了一下就在内存中开辟了一个空间,所有它的内存地址是有的就不等于nil了,s2则反之并没有开辟内存空间所以是nil,这里说明一下切片是引用类型必须要先申明内存空间之后才能使用

Srim_ycb
Srim_ycb · #17 · 3年之前

首先,go的函数传参,返回都是拷贝。你可以把它理解成一个浅拷贝,而slice底层确实是结构体,函数修改的虽然是拷贝的数据,但是由于是浅拷贝,原来的slice和函数里的slice虽然不一样,但是共用同一个数组,只是通过下标修改值当然不会影响到底层的数组了啊!append扩容了之后,函数里面的slice绑定了一个新数组,当然不会影响到元slice了。1.PNG2.PNG

ws1992go
ws1992go · #18 · 3年之前

对应的内存地址空间不一样,所以不相等。

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传