为什么在 Go 数组(array)被设计成值,而不跟 C/C++或 Java 一样,设计为一个引用?
a = [1,2,3]
b = a[:]
b[0] = 0
print(a) # [1,2,3]
print(b) # [0,2,3]
go slice:
a := []int{1,2,3}
b := a[:]
b[0] = 0
fmt.Println(a) // [0, 2, 3]
这就是区别了
var a = new[] { 1, 2, 3 };
var b = a[..];
b[0] = 0;
Console.WriteLine(a); // [1,2,3]
Console.WriteLine(b); // [0,2,3]
slice 经常被说成「引用类型」是因为它本质上是这样的 struct:
type slice struct {
array unsafe.Pointer
len int
cap int
}
传递 slice 等同于传递这样的一个 struct 值,只是值里面有一个指向底层 array 的指针,所以真正存放内容的 array 部分没有被 copy 。如果直接传一个 array 值,里面存放的内容是会被 copy 一次的。
没有指针的语言才需要弄出一个什么引用的概念,有指针就不需要引用这个概念了呀。
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
于是就有一堆这样的迷惑行为产生了,(后面引战话我删了,逃
引用类型、值类型 和 引用传递、值传递是完全不同的两个概念,go 只有值传递,但是既有值类型也有引用类型。
interface 、function 、point 也是引用类型。甚至 string 在实现上也是也是引用类型。
一般情况下
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#应该差不多.
然后 std::array 是值语义,一看你就没好好看 c++。
golang 也是一样的道理,只不过换了个名字。
只能说它就是这样设计的. 掌握惯用法就行了.
我个人比较喜欢 c/c++的那种方法,数组就是指针,蛮好理解.