此處數(shù)組只講Go語言中和C語言不一樣的地方
- 格式不同:
Go語言定義數(shù)組的格式:var ages [3]int
定義的同時初始化
格式 var 數(shù)組名稱 數(shù)組類型 = 數(shù)組類型{值}
var ages [3]int = [3]int{1,3,5}
-
注意點:
1.在定義的同時初始化必須在大括號前寫上數(shù)據(jù)類型,并且數(shù)據(jù)類型必須相同(數(shù)組長度和數(shù)據(jù)類型)2.可以和C語言一樣部分初始化,但是C語言部分初始化沒有被賦值的地方保存了垃圾數(shù)據(jù),Go語言中沒有被賦值的地方默認(rèn)為0\
3.數(shù)組中數(shù)組的名稱不能代表數(shù)組的地址
- 第二種方式(省略數(shù)組長度)
ages := [...]int{1,3,5}
- 在這里用...表示數(shù)組長度,編譯器根據(jù)賦值的元素個數(shù)自動計算數(shù)組的元素個數(shù)
先定義后初始化
var ages [3]int
ages = [3]int{1,3,5}
- 在這里,Go語言可以一次性給數(shù)組賦值,當(dāng)然也可以逐個賦值
數(shù)組注意點
- Go語言中數(shù)組是值類型,數(shù)組之間的賦值是值拷貝,而不是地址的拷貝
ages := [3]int{1,3,5}
change(ages)
fmt.println(ages)//1,3,5
func change(num [3]int) {
num[1] = 666
}
2.由于數(shù)組是值類型,所以兩個數(shù)組間可以用" == " " != "進(jìn)行比較,但是進(jìn)行值比較的數(shù)組必須類型長度完全相同,完全相同!!!
數(shù)組的遍歷
- 方法一: 傳統(tǒng)的for循環(huán)
- 方法二: for index,value := range ages{
fmt.println(index,value)
}
二維數(shù)組
- 定義二維數(shù)組的格式
var 數(shù)組名稱 [行數(shù)][列數(shù)]數(shù)據(jù)類型
var 數(shù)組名稱 [一維數(shù)組的個數(shù)][每個一維數(shù)組的元素個數(shù)]數(shù)據(jù)類型 - 注意點:
二維數(shù)組也可以省略元素個數(shù), 但是只能省略行數(shù), 不能省略列數(shù)
切片(重點)
- 數(shù)組的長度一旦固定,就不能改變,Go語言為了解決這一問題,產(chǎn)生了切片,切片就是可變的數(shù)組
- 切片的底層是結(jié)構(gòu)體
Go語言源碼中如此展示
type slice struct{
array unsafe.Pointer // 指向底層數(shù)組指針
len int // 切片長度(保存了多少個元素)
cap int // 切片容量(可以保存多少個元素)
}
創(chuàng)建切片的方式
-
方法一: 通過數(shù)組來創(chuàng)造切片
- 格式:[起始位置:結(jié)束位置] 包括起始位置但不包括結(jié)束位置,左閉右開的區(qū)間
- 注意點:
切片的長度(len)等于截取到的數(shù)組的長度
切片的容量(cap)等于數(shù)組的長度減去切片的起始位置
如果不寫起始位置,就是從第0個開始截取
如果不寫結(jié)束位置,就是截取到末尾
var ages[5] = [5]int{1,3,5,7,9}
var sce[] = ages[2:]
fmt.println(sce)//[5,7,9]
fmt.println(len(sce))//3
fmt.println(cap(sce))//3
var sce2[] = ages[:2]
fmt.println(sce2)//[1,3]
fmt.println(len(sce2))//2
fmt.println(cap(sce2))//5
- 方法二: 通過make函數(shù)創(chuàng)建
-
格式:make([]數(shù)據(jù)類型,長度,容量)
第一個參數(shù): 創(chuàng)建的數(shù)據(jù)類型,創(chuàng)建切片前一定要加[]
第二個參數(shù): 告訴系統(tǒng)創(chuàng)建切片的長度
3.第三個參數(shù): 告訴系統(tǒng)創(chuàng)建切片的容量(如果第三個參數(shù)不寫,那么第三個長度默認(rèn)和切片的長度相等)
-
var sce []int = make([]int, 2)
fmt.Println(sce)//[0,0]
fmt.Println(len(sce))//2
fmt.Println(cap(sce))//2
- 方法三: 通過Go語言提供的語法糖創(chuàng)建
-
中括號沒有值就是切片
- 特點:用Go語法糖創(chuàng)建的切片的長度和容量相等
- var ages []int = []int{1,3,5,7,9}
-
切片的使用
- 使用方法一: 切片是可變長度的數(shù)組,所以可以像數(shù)組一樣使用切片,但是索引的長度不能超過切片的len
var sce []int = make([]int,2,5)
sce[2] = 999//報錯,索引大于切片長度
fmt.println(sce)
-
使用方法二: 可以通過預(yù)先定義好的函數(shù)來使用切片
-
append函數(shù)
append(切片,數(shù)據(jù))
append函數(shù)作用是在切片len后面添加數(shù)據(jù)
-
-
注意點:
在append函數(shù)后面追加的數(shù)據(jù)超過了切片的cap,那么切片會按照當(dāng)前容量*2的方式重新擴(kuò)容,并且sce[0]的值和擴(kuò)容前的值相等,說明將源切片拷貝了一份作為新切片,切片的地址會發(fā)生改變
append函數(shù)返回一個切片,由于append擴(kuò)容的時候需要創(chuàng)建一個新的切片,所以返回一個切片
如果容量不超過1024時,擴(kuò)容的時候會按照cap*2擴(kuò)容,如果容量超過1024,擴(kuò)容的時候會按照原來cap的1/2擴(kuò)容
如果append追加的數(shù)據(jù)未超過切片的cap,雖然返回一個切片,但是切片的地址沒有改變,如果append追加的數(shù)據(jù)超過了切片的cap,那么返回的切片的地址會發(fā)生改變
var sce []int = make([]int, 2, 3)
fmt.Printf("%p\n",sce)//0xc0420520c0
sce = append(sce, 3)
fmt.Printf("%p",sce)//0xc0420520c0
var sce2 []int = make([]int, 2, 2)
fmt.Printf("%p\n",sce)//0xc0420080c0
sce = append(sce2, 3)
fmt.Printf("%p",sce2)//0xc04200e300
- 使用方法三:
- 切片的增刪改查
增(append函數(shù))
改(切片名稱[索引] = 值)
-
查
for _,value in range 切片名稱{
if value == 3 {
fmt.Println("包含數(shù)字3")
}
}- 刪
- 我們不僅可以通過數(shù)組創(chuàng)建切片,還能通過切片創(chuàng)建切片
- 通過切片創(chuàng)建的切片,兩個切片指向同一個數(shù)組
- 切片的刪就是將第二次截取的切片添加到第一次截取的切片后面即可
- append(sce[:index],sce[index+1:])
- 刪
- 切片的增刪改查
var ages []int = []int{1,3,5,7,9,11}
//假如需要刪掉的索引是3(值為7)
var sce []int = ages[:3]
var sce2 []int = ages[4:]
sce3 = append(sce,sce2)
fmt.Println(sce)//[1,3,5,9,11]
數(shù)組和切片的辨別
- 只要定義的時候中括號里有東西,哪怕是...,那定義的就是數(shù)組不是切片
切片和函數(shù)
- 可變參數(shù)的函數(shù),其中可變參數(shù)底層原理就是切片
- 注意點: 如果函數(shù)編寫了可變參數(shù), 那么可變參數(shù)只能放在形參列表的最后
res:=sum(10, 20, 30)
fmt.Println(res)//60
}
// 注意點: 如果函數(shù)編寫了可變參數(shù), 那么可變參數(shù)只能放在形參列表的最后
func sum(nums ...int) int{
//func sum(value float32, nums ...int) int{
var res int
for _,value := range nums {
res += value
fmt.Println(value)
}
return res
}
func sum(nums []int) int{
// 2.遍歷切片
var res int
for _, value := range nums{
res += value
}
return res
}
- copy函數(shù)
copy(目標(biāo)切片, 源切片)
-
注意點:
如果目標(biāo)切片的容量大于了源切片的容量,那么源切片會一一將數(shù)據(jù)覆蓋到目標(biāo)切片上
如果目標(biāo)切片的容量小于了源切片的容量,那么源切片會一一將數(shù)據(jù)覆蓋到目標(biāo)切片上,多余的數(shù)據(jù)會被剔除
sce1 := []int{1, 3} // 源切片
sce2 := []int{2, 4, 6} // 目標(biāo)切片
fmt.Println(sce2)
copy(sce1, sce2)
fmt.Println(sce1)//[2,4]
切片的注意點
1.切片可以再生成新的切片, 兩個切片底層指向同一個數(shù)組
2.切片和數(shù)組不同, 切片不支持== !=操作
3.在Go語言中字符串的底層實現(xiàn)就是切片, 所以可以通過字符串來生成切片
//需求:將字符串"www.it666.com"的首字母改為M
var str string = "www.it666.com"
sce := make([]byte,len(str))
copy(sce,str)
sce[0] = 'M'
fmt.Printf("%s\n", sce)
-
通過數(shù)組創(chuàng)建切片補充
格式: 數(shù)組名稱[low: high: max]
注意點:
max必須大于第二個參數(shù)
如果max不寫,那么cap就等于數(shù)組長度 - 第一個參數(shù)
如果寫上max,那么cap就等于max - 第一個參數(shù)
var arr [6]int = [6]int{1,3,5,7,9,11}
var sce []int = arr[2:4:4]
fmt.Println(sce) // [5,7]
fmt.Println(len(sce)) // 2
fmt.Println(cap(sce)) // 2
- 僅僅定義, 沒有初識的數(shù)組是可以使用的, 但是僅僅定義但是沒有初始化的切片是不能使用的
var sce []int
//sce[0] = 666 編譯報錯
//sce[1] = 888 編譯報錯
sce = make([]int, 3, 5)
sce[0] = 666
sce[1] = 888
sce[2] = 999
fmt.Println(sce) // [666,888,999]
var sce []int
sce = append(sce, 1)
sce = append(sce, 2)
sce = append(sce, 3)
fmt.Println(sce) // [1,2,3]
- 切片計算長度不能用unsafe.Sizeof(sce),切片本質(zhì)是一個結(jié)構(gòu)體,所以Sizeof實際上計算的是結(jié)構(gòu)體的內(nèi)存,在64位機(jī)上為24
var sce []int = []int{1, 3, 5, 7, 9}
//推薦方式:
length := len(sce)
fmt.Println(length)
數(shù)組和切片作為函數(shù)參數(shù)的區(qū)別
數(shù)組作為函數(shù)參數(shù),是值傳遞,函數(shù)內(nèi)改變不會改變實參的值
切片作為函數(shù)參數(shù),是地址傳遞,函數(shù)內(nèi)改變會改變實參的值