1. 程式人生 > >go語言陣列和切片的深度對比

go語言陣列和切片的深度對比

首先說下,陣列和切片由於語法十分相似,在使用中容易混淆,要認真區分。實際上陣列和切片是兩種完全不同的型別。
版本是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的,看來完全不是這回事。。