为什么在 Go 数组(array)被设计成值,而不跟 C/C++或 Java 一样,设计为一个引用?

2020-05-19 10:32:36 +08:00
 frankhuu
最近在入门学习 Go 中的 array 和 slice,能初步理解两者的区别,但是总是差一点意思,为什么数组要设计为值?为什么 Go 不能像 Java 等其他语言一样没有 slice,slice 究竟有多大的威力?
6618 次点击
所在节点    Go 编程语言
42 条回复
mornlight
2020-05-19 16:57:27 +08:00
@xhp281 #13 值的体积比较大时,的确传指针会快一些,具体看场景。小值传指针可能会加重 GC 负担。
传参用 value 还是 pointer,官方有建议的做法: https://github.com/golang/go/wiki/CodeReviewComments#receiver-type
cmdOptionKana
2020-05-19 17:01:19 +08:00
主要是因为 Go 有指针。

没有指针的语言才需要弄出一个什么引用的概念,有指针就不需要引用这个概念了呀。
Threeinchtime
2020-05-19 18:07:21 +08:00
Go 语言只有值传递,channel,slice,map 是引用类型,其余是值类型。
传值类型拷贝内存,传引用类型拷贝指针。
jaylee4869
2020-05-19 18:18:16 +08:00
巧了我今天刚开始看 Go,碰巧也看到这里了
CreSim
2020-05-19 18:30:24 +08:00
主要需要考虑的是栈上分配还是堆上分配吧,array 在栈上,slice 在堆上
wysnylc
2020-05-19 18:40:38 +08:00
slice 和 ArrayList 等价,底层都是数组,在数组超出长度的时候创建新的数组把原来的复制过去
ica10888
2020-05-19 18:57:21 +08:00
前面没解释清楚,我这里详细说一下
array 和 slice 是两个完全不一样的东西,slice 的底层实现类似 ArrayList,是用到了 array 数组的拷贝,扩容生成一个新数组
另外,如果理解了这道经典题的输出的迷惑行为,就大概知道 slice 是什么了
func main() {
s := []int{5}
fmt.Println(cap(s)) //1

s = append(s, 7)
fmt.Println(cap(s)) //2

s = append(s, 9)
fmt.Println(cap(s)) //4

x := append(s, 11)
y := append(s, 12)

fmt.Println(s, x, y) //[5 7 9] [5 7 9 12] [5 7 9 12]
}
x,y 都指向同一个 slice,ops,引用类型
这里和引用类型和值类型没啥关系,和指针也没啥关系,数组本来就应该是值类型的,而 slice 又和数组长得一模一样
至于 slice 为什么和数组用一套语法,就在于 golang 语言没法像 java 那样写出 ArrayList<String>这样来约束类型了,不知道是谁的主意,直接在语法层面解决了这一问题,关键是还和数组用同一套,就是因为没有泛型写不出一个 ArrayList 的类出来,同理还有 map
于是就有一堆这样的迷惑行为产生了,(后面引战话我删了,逃
qW7bo2FbzbC0
2020-05-19 19:11:44 +08:00
@mornlight 谢谢,
qW7bo2FbzbC0
2020-05-19 19:12:01 +08:00
还是不太懂
SimbaPeng
2020-05-19 20:47:29 +08:00
@mornlight
@Hellert

引用类型、值类型 和 引用传递、值传递是完全不同的两个概念,go 只有值传递,但是既有值类型也有引用类型。
SimbaPeng
2020-05-19 20:52:50 +08:00
@Threeinchtime

interface 、function 、point 也是引用类型。甚至 string 在实现上也是也是引用类型。
guonaihong
2020-05-19 21:44:29 +08:00
go 有指针,实在没必要所有类型都是引用。
想要引用,无非 type 加个*。指针这个大杀器已经给你,就看你怎么玩了。
damingxing
2020-05-19 22:35:41 +08:00
Go 语言就是这么设计的. 只能熟悉它的惯用法.

一般情况下
arr := [LEN]int{}
这里 arr 表示一个数组,值类型

用 arr2 := arr
这复制了一个数组,相当于重新分配一个相同大小的空间,然后把每个数拷贝进去


如果用
arr := &[LEN]int{}
这个地方就是一个指向数组的指针.

arr2:=arr
复制一个指针, arr2 和 arr 指向同一个地址.

然后有点诡异的地方来了.
这里你可以用 arr[0]=2, 等同于 (*arr)[0]=2.
我是不太喜欢前面的写法的. 容易让人以为 arr 是一个数组而不是一个指针.
相反在 c/c++里就没有这种困惑了. 因为在 c/c++里数组本质就是一个指针.
在 c#里面也比较好理解,数组是一个引用.

关于 Slice 相反是比较容易理解的. 因为 Slice 始终是一个引用.
相比于 c/c++就是 vector.
相比于 c#就是 ArrayList. 或者一个缺少功能的数组.

Java 和 c#应该差不多.
damingxing
2020-05-19 22:40:25 +08:00
如果用数组作为参数,则建议始终传递指针.
读别人代码时不理解的地方写两行代码测试一下即可.
Austaras
2020-05-20 00:56:36 +08:00
因为 rob pike 是个只会写 c 的民科
TransAM
2020-05-20 01:04:51 +08:00
golang 里的 slice 是引用,还是有选择空间的。

然后 std::array 是值语义,一看你就没好好看 c++。
TransAM
2020-05-20 01:10:04 +08:00
你平时是否能用到 std::array,和 std::vector 相比如何?

golang 也是一样的道理,只不过换了个名字。
TransAM
2020-05-20 01:19:49 +08:00
@keepeye a 和 b 都是一个类型,而且 python 中又不止一个数组实现,你随便换个扁平序列,比如 np.array,就是第二种了。
movistar
2020-05-20 01:26:56 +08:00
Go 在设计的时候感觉就有问题,重复应用关键字,就为了省几个关键字...
结果一个关键字在不同场景有不同的实现,导致看起来很迷
大道至简.....
而且 Go 目标就一个更容易开发的 C,有的其他语言做的好的优点也不采纳.所以其他语言转过来的会觉得很奇怪
至于 slice 和 array 的问题,在 Java 里就是 ArrayList 和 type[]的区别...
除了特殊场景(定长),绝大部分人用 ArrayList...
frankhuu
2020-05-20 10:20:05 +08:00
@damingxing 为什么 Go 不直接将 array 设计为引用类型,而是值类型呢?然后再设计一个类似 ArrayList 的类来实现动态伸缩列表

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/673135

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX