Go 數組與切片

#### Go 數組與切片
***渡風渡塵緣,修行亦修心***
##### 數組
數組是能夠存放多個同一類型的數據的集合,數組也是一種數據類型,它是值類型;
數組如何定義?
var 數組名 [數組大小]數據類型: var a [5]int
package main

import "fmt"

func main(){
   var intArr [3]int
   intArr[0] = 10
   intArr[1] = 20
   intArr[2] = 30
   fmt.Println(intArr)
}

  


數組在內存中的佈局:

  
1. 數組的地址能夠經過數組名獲取: &intArr;
2. 數組的第一個元素地址就是數組的地址:;
3. 數組的各個元素的地址間隔根據數組的類型決定: int64-> 8 字節,int32-> 4字節;
4. 數組若是未給元素賦值,元素的默認值爲類型的零值: int64->0,string->"",float64->0;
5. 訪問數組中的元素: 使用下標索引方式(左閉右開方式),這個與其它語言同樣,超出下標索引將會出錯,索引越界;
6. 數組的遍歷有兩種方式: for 傳統方式; for range 方式 ;
package main

import "fmt"

func main(){
   var a [5]int
   a[0] = 1
   a[1] = 2
   a[2] = 3
   // 默認值: a[3] = 0 , a[4] = 0
   // 下標越界: a[5] = 10  // 錯誤, 下標越界
   fmt.Println(a) //[1 2 3 0 0 ]
   fmt.Printf("a address is %p\n",&a) // 0xc000078030
   fmt.Printf("a[0] address is %p\n",&a[0]) //0xc000078030
   fmt.Printf("a[1] address is %p\n",&a[1]) //0xc000078038
   fmt.Printf("a[2] address is %p\n",&a[2]) //0xc000078040
   // 數組的遍歷方式
   for i:=0;i<len(a);i++{
      fmt.Println(a[i])
   }
   // for range 方式 
   for k,v := range a {
      fmt.Println(a[k])
      fmt.Println(v)  // 這個值是元素的副本, 因此不建議在v 上操做: 取址或賦值;  
   }
}

  


---
數組有多種初始化方式:
import "fmt"

func main(){
   // 1. 字面量直接賦值
   var a [5]int = [5]int{1,2,3,4,5}
   var b = [5]int{6,7,8,9,0}
   // 2. 數組長度由編譯器推導
   var c = [...]int{1,2,3,4,5,6}
   // 3. 根據索引位置直接賦值
   var d = [...]int{1:1,10:0,3:3}
   // 4. 類型推導
   f := [...]int{1:1,9:9,10:0}
   fmt.Println(a)
   fmt.Println(b)
   fmt.Println(c)
   fmt.Println(d)
   fmt.Println(f)
}

  


---
數組使用注意事項
1. 數組是多個相同類型的集合,一旦聲明或者定義了,數組的長度將是固定不變的,不能動態變化 ;
2. 數組中的元素能夠是任意類型,能夠是值類型,也能夠是引用類型,不能夠混用;
3. 數組建立後若是不賦值,將採用類型的零值 ;
4. 數組屬於值類型, 在默認狀況下是傳遞數組是值傳遞,因此數組之間不會影響;
5. 若是須要更改原數組,須要傳遞數組的地址(相似引用傳遞);
6. 長度是數組類型的一部分,在傳遞參數時須要注意數組的長度;
package main

import "fmt"

func test01(a [5]int){
   a[1] = 20
   fmt.Println(a)  //[1 20 3 4 5]
}
func test02(a *[5]int){
   a[1] = 20
   fmt.Println(*a) // [1 20 3 4 5]
}
func main(){
   var a = [5]int{1,2,3,4,5}
   test01(a)
   fmt.Println(a) // [1 2 3 4 5]
   // 若是須要修改原數組的值,須要傳遞數組的地址
   test02(&a)
   fmt.Println(a) // [1 20 3 4 5]
   // 傳遞參數時須要考慮數組的長度
   //var b = [3]int{1,2,3}
   //test01(b) // 錯誤, 數組的長度不正確
}

  


在Go 編程中,數組的使用仍是不太多, 由於不能動態的擴容,數組通常做爲已經肯定的數據使用;
##### 切片
1. 切片是數組的一個引用, 因此切片屬於引用類型,在進行參數傳遞時,屬於引用傳遞;
2. 切片的使用與數組相似: 遍歷,訪問等;
3. 切片的長度是能夠動態變化的,因此也能夠說切片是動態變化的數組;
4. 切片的基本語法 :
var 切片名 []類型
package main

import "fmt"

func main(){
   // 這裏只演示切片如何使用,稍後再介紹切片如何聲明與賦值操做
   // 聲明並定義一個數組
   var a = [5]int{1,2,3,4,5}
   // 切片s 引用已經存在的數組a
   var s = a[:] // 這種方式引用全部的數組元素,也可引用一部分數組元素 var s = a[1:3]
   fmt.Printf("%T\n",s)
   fmt.Println(s)
   fmt.Println(len(s)) // 5 切片的長度
   fmt.Println(cap(s)) // 5 切片的容量,容量和長度是能夠動態變化;
}

  


---
切片在內存中的佈局

 
1. slice 是一下引用類型;
2. 從底層上講,slice 實際上就是一個結構體(稍後會學習到)
type slice struct {
ptr *[2]int
len int
cap int
}
切片的初始化
package main

import "fmt"

func main(){
   // 方式1 建立一個數組,讓切片引用這個數組
   var a = [5]int{1,2,3,4,5}
   var s = a[:]
   // 同數組同樣, 在賦值或取值時,仍然須要注意切片下標索引不可越界
   //s[10] = 0
   s[4] = 50
   fmt.Println(s)
   // 方式2
   // 切片是引用類型,因此聲明後須要make 分配內存
   var b []int
   // make 分配內存,長度能夠指定任意值,建議根據要存儲的數據長度合適定義
   // make 參數,還能夠定義切片的容量,若是定義了切片的容量,則要求容量大於切片的長度
   b = make([]int,5)
   fmt.Println(b)
   // 方式3
   // 字面量方式,聲明後直接賦值
   var c = []int{1,2,3,4,5}
   fmt.Println(c)
}

  


1. 方式1: 直接引用已經存在的數組,開發者對底層是可見的;
2. 方式2: 經過make 建立的切片,程序也會建立一個數組,不過這個數組對開發者不可見,由程序本身維護;
3. 方式3: 相似make;
---
切片使用的注意事項:
1. 切片的遍歷與訪問同數組同樣, 這裏就不寫了;
2. 切片定義後,不能直接使用,須要引用一個數組或make 分配內存給切片使用;
3. 使用append 函數能夠對切片進行動態擴容;
4. 使用copy 函數能夠對切片進行拷貝,拷貝後的切片是獨立的, 在新的切片上操做對原切片沒有影響;
5. 切片是引用類型,在給函數傳遞參數(切片)時,屬於引用傳遞;
package main

import "fmt"

func test01(a []int){
   a[0] = 100
   fmt.Println(a)
}
func main(){
   var a = []int{1,2,3}
   fmt.Println(a)
   // 使用append 函數將切片擴容
   a = append(a,4)
   fmt.Println(a)
   // 使用copy 函數進行切片複製
   // 在使用copy 函數時須要注意如下幾點:
   //1. 目標(dst)必須是已經聲明並初始化過的
   //2. 若是目標的長度小於被複制的切片長度,則以目標的長度爲準複製,超出部分不復制;
   var b []int
   b = make([]int,3)// 超出元素4,將不會被複制
   copy(b,a)
   fmt.Println(b)
   // 對新切片的操做不會影響到原來的切片
   b[1] = 10
   fmt.Println(a)
   fmt.Println(b)
   // 切片屬於引用傳遞
   test01(a)
   fmt.Println(a)
}

  


---
string 與slice
在學習基本數據類型時咱們學習了string 類型,知道它是由多個字節鏈接在一塊兒的字符串;
實際上string 底層是一個字節數組,因此string 也能夠進行切片操做;
可是string 是不可變的, 因此不能string[0]='x', 這種賦值操做; 若是須要更改,須要將string 轉爲[]byte 切片,更改完成後再轉爲string 類型;
package main

import "fmt"

func main(){
   var a = "abcdef"
   // 能夠進行切片處理
   fmt.Println(a[:3])
   // 不能夠更改string 的值
   // a[1] = 'f' //這是錯誤的
   // 須要將string 轉爲 []byte 切片才能夠操做
   var arr []byte
   arr = []byte(a)
   arr[1] = 'f'
   a = string(arr) // 更改完成後再將切片轉爲string 類型
   fmt.Println(a)
   // []byte 只能處理英文與數字,處理其它語言時須要將string 轉爲[]rune 切片
   var b = "你好啊"
   var arr2 []rune
   arr2 = []rune(b)
   arr2[0] = '我'
   b = string(arr2)
   fmt.Println(b)
}

  我的微信公衆號上有最新的文章,歡迎你們關注,一同交流學習編程

相關文章
相關標籤/搜索