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語言中一般不直接使用陣列,而是使用切片,關於切片的部分,下一章再來講。