1. 程式人生 > >GO學習筆記——陣列(10)

GO學習筆記——陣列(10)

今天來看看陣列。C/C++還有其他一些語言基本都用方括號 [] 來表示陣列,另外C++還有array陣列容器以及vector動態順序表(這其實相當於後面要說到的切片)。

當然了,今天的主題是GO的陣列。

GO也是用方括號 [] 來表示陣列,先來看下它的一些定義方式

func main() {
	//1. 聲明瞭一個包含5個int的陣列,每個元素被賦零值0
	var arr1 [5]int

	//2. 定義了一個包含5個int的陣列,前三個元素分別被賦值為1,2,3,後兩個元素被賦零值0
	arr2 := [5]int{1,2,3}

	//3. 用...來省略個數,這裡就是定義了一個包含5個int的陣列,編譯器幫我們計算這個陣列的大小
	arr3 := [...]int{1,2,3,4,5}

	//4. 定義一個四行五列的二維陣列,且第一行被賦值為全1
	arr4 := [4][5]int{{1,1,1,1,1}}

	fmt.Println(arr1)
	fmt.Println(arr2)
	fmt.Println(arr3)
	fmt.Println(arr4)
}
[0 0 0 0 0]
[1 2 3 0 0]
[1 2 3 4 5]
[[1 1 1 1 1] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]

以上幾種定義方式是較為常見的定義方式,總結一下

  • 遵循GO基本的變數命名規則,陣列名現在前面,陣列的元素型別寫在後面
  • 陣列的元素個數寫在型別前面,用 [n]type 表示
  • []內可用...表示讓編譯器計算陣列個數

下面來說一些陣列使用的技巧

遍歷陣列的range關鍵字

一般在C語言中,遍歷一個數組都是用類似如下的遍歷方式,GO語言中也是一樣

func main() {
	arr := [5]int{1,2,3,4,5}
	for i := 0; i < 5; i++ {
		fmt.Print(arr[i])
	}
}
//輸出結果
12345

 但是GO語言中有一個專門用於遍歷陣列的關鍵字range,可以方便我們遍歷陣列,用法如下

func main() {
	arr := [5]int{1,2,3,4,5}
	for i,v := range arr {
		fmt.Printf("第%d個元素是: %d\n",i,v)
	}
}

//輸出結果
第0個元素是: 1
第1個元素是: 2
第2個元素是: 3
第3個元素是: 4
第4個元素是: 5

這就有點類似於C++11中引入的範圍for迴圈了,其實很多語言都引入了這種類似的範圍for迴圈來簡化傳統for迴圈的寫法。

range返回兩個值,它返回的第一個引數是索引,第二個引數是該索引的值

,所以我們可以像上面一樣用兩個變數來接收它們。

正式因為range能夠同時返回索引以及索引的值的特性,所以在遍歷陣列的時候,基本上都使用range來遍歷,方便直觀。

當然,這裡有一個問題,有時候我們只想要索引的值,而不想要該索引,那麼怎麼辦,那麼就可以使用下劃線_了。

func main() {
	//用下劃線來不接收索引,而只接收索引的值
	arr := [5]int{1,2,3,4,5}
	for _,v := range arr {
		fmt.Println(v)
	}
}

//輸出結果
1
2
3
4
5

如果只要索引,那麼就不用這麼麻煩了,直接用一個返回值來接收就可以了

func main() {
	//用下劃線來不接收索引,而只接收索引的值
	arr := [5]int{1,2,3,4,5}
	for i := range arr {
		fmt.Println(arr[i])
	}
}

//輸出結果
1
2
3
4
5

陣列的長度

可以通過len函式來獲得陣列的長度

func main() {
	arr := [5]int{1,2,3,4,5}
	fmt.Println(len(arr))
}

輸出結果

5

陣列是值型別的

先上一段C++的程式碼

#include <iostream>
using namespace std;

void func(int *arr){
    arr[0] = 100;    //這裡將陣列的第一個元素改為100
}
int main(){
    int arr[] = {1,2,3,4,5};
    func(arr);
    for(int i = 0; i < 5; ++i)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}

輸出結果

100 2 3 4 5 

可以看到,我們將這個陣列作為引數傳給了函式func,並在函式體內修改了第一個陣列元素的值,我們發現函式執行完以後,原來的陣列的第一個元素被改變了,這就是引用型別的。C++函式引數型別要傳陣列的時候,會弱化成陣列首元素的地址傳進去,也就是說,函式拿到的是原來的陣列的首元素的地址,也就是一個指標,這樣進行改動,當然會改變原陣列的值。

但是GO就不是了,GO的陣列是值型別的。先把上述程式碼改成GO版本的。

//注意下面必須是[5]int,GO編譯器認為[3]int和[5]int是兩種不同的資料型別
func fun(arr [5]int){
	arr[0] = 100
}

func main() {
	arr := [5]int{1,2,3,4,5}
	fun(arr)
	for i := range arr {
		fmt.Printf("%d ",arr[i])
	}
}

輸出結果

1 2 3 4 5

這邊GO就沒有對元素進行改變,也就是說,在GO語言中,陣列是值型別的。其實,在陣列傳參的時候,因為只有值傳遞,所以是拷貝了一個一模一樣的陣列作為臨時的引數傳給函式的,所以這是兩個陣列,臨時陣列改變不影響原來的陣列。

那麼我要是想改變原來的陣列內容怎麼辦呢?很簡單,用指標,傳引數的時候傳一個指標就好了。

//注意下面必須是[5]int,GO編譯器認為[3]int和[5]int是兩種不同的資料型別

func fun(arr *[5]int){    //這裡傳一個指標
	arr[0] = 100
}

func main() {
	arr := [5]int{1,2,3,4,5}
	fun(&arr)    //這裡傳陣列的的地址
	for i := range arr {
		fmt.Printf("%d ",arr[i])
	}
}

輸出結果

100 2 3 4 5 

可以看到,這樣原陣列就被改變了。

所以,這裡關於陣列給函式傳參的時候的用法,需要注意,到底是要傳值呢,還是要傳指標,需要根據程式來不同設計。

但是,這樣傳引數還是很麻煩啊,我們必須得提前知道陣列的個數,因為不同個數的陣列是被認為不同型別的。

所以在GO語言中一般不直接使用陣列,而是使用切片,關於切片的部分,下一章再來講。