go語言陣列和切片的深度對比
阿新 • • 發佈:2019-01-04
首先說下,陣列和切片由於語法十分相似,在使用中容易混淆,要認真區分。實際上陣列和切片是兩種完全不同的型別。
版本是go-1.6,沒有用最新的,可能新版本會對go有所優化把。
var a [3]string //陣列
var b []string //切片
從語法上來看,陣列遵循傳統的三要素 – 名稱、型別、長度。
而切片只有名稱、型別,這意味著切片是不定長的。
從記憶體的角度來看,資料是一整塊連續的、固定長度、固定位置的記憶體。
而切片則是一個指標,指向一塊記憶體,當容量不夠時就開闢更大的記憶體。
因此,陣列還是傳統意義上的資料,而切片則更類似於C++中的vector。
陣列使用時的注意點:
陣列的賦值代價是非常大的,相當於把一塊記憶體完全拷貝。
切片使用時的注意點:
切片結構中使用了指標,存在深拷貝問題。
實驗:
package main
import (
"fmt"
)
func main() {
//切片
a := []string{"aaa", "bbb", "ccc"}
b := a
a[0] = "aaa"
a[1] = "ccc"
fmt.Println(a, b, &a[0], &b[0])
//陣列
f := [3]string{"aaa" , "bbb", "ccc"}
g := f
fmt.Println(f, g, &f[0], &g[0])
//互相賦值會報錯
//a = f
//./main.go:20: cannot use f (type [3]string) as type []string in assignment
//記憶體
c := "aaa"
d := "aaa"
fmt.Println(&c, &d)
}
結果:
[aaa ccc ccc] [aaa ccc ccc] 0xc82000c120 0xc82000c120 //切片
[aaa bbb ccc] [aaa bbb ccc] 0xc82000c150 0xc82000c180 //陣列
0xc82000a380 0xc82000a390 //字串指標
由此可見,切片確實是淺拷貝的。而陣列則不存在這樣的問題。
另外切片和陣列是兩種不同型別,不能互相賦值。
最後測試了一下字串,發現和C中的字串不一樣。C中這兩個字串應該指向同一塊記憶體的。記得學C的時候,C中的字串叫字面值常量,儲存在常量區。至於go的不是太清楚了。不過感覺有點浪費。
C字串實驗:
#include <stdio.h>
int main()
{
char *a = "aaa";
char *b = "aaa";
printf("%p,%p\n", a, b);
printf("Hello world\n");
return 0;
}
結果:
0x400634,0x400634
Hello world
append函式到底做了什麼?
執行如下程式碼:
h := []string{"aaa"}
h2 := append(h, "bbb")
fmt.Println(&h[0], &h2[0])
結果:
0xc82000a3a0 0xc82000e1a0
貌似就是新開闢了一塊記憶體額,記得C中有realloc的,看來完全不是這回事。。