声明数组的方式
固定长度的数组在传递参数时需严格匹配数组类型、长度
函数的形参为数组时,其拷贝方式为值拷贝
`package main import ( "fmt" ) func printArray(myArray [4]int) { // 值拷贝 for index, value := range myArray { fmt.Println("index = ", index, " , value = ", value) } myArray[0] = 233 } func main() { // 固定长度数组 var myArray1 [10]int var myArray2 [10]float32 myArray3 := [10]int{1, 2, 3, 4} myArray4 := [4]int{11, 22, 33, 44} // for i := 0; i < 10; i ++ { for i := 0; i < len(myArray1); i++ { fmt.Println(myArray1[i]) } fmt.Println("===== ===== ===== =====") for j := 0; j < len(myArray2); j++ { fmt.Println(myArray2[j]) } fmt.Println("===== ===== ===== =====") for index, value := range myArray3 { fmt.Println("index= ", index, ", value = ", value) } // 查看数组的数据类型 fmt.Printf("myArray1 types = %T\n", myArray1) fmt.Printf("myArray2 types = %T\n", myArray2) fmt.Printf("myArray3 types = %T\n", myArray3) fmt.Printf("myArray4 types = %T\n", myArray4) fmt.Println("===== ===== ===== =====") printArray(myArray4) fmt.Println("=====") for index, value := range myArray4 { fmt.Println("index = ", index, ", value = ", value) } } `
image
image
slice 切片,动态数组。
在定义函数时,我们通常希望形参的数据类型更通用,数组是固定长度的,slice 切片是动态数组,形参直接定义为 slice 即可。
动态数组在函数传参时为引用传递
不同元素长度的动态数组,传参的形参是一致的
`package main import ( "fmt" ) // func printArray(myArray [4]int) { // // 值拷贝 // for index, value := range myArray { // fmt.Println("index = ", index, " , value = ", value) // } // myArray[0] = 233 // } func printSlice(myArray []int) { // 引用传递 // Slice 和 数组相比的一个好处是可变长度,这为形参的参数值长度提供了更多的可能性,更加灵活了 // 动态数组是指向数组内存的指针(引用) for _, value := range myArray { fmt.Println("value = ", value) } myArray[0] = 233 } func main() { // // 固定长度数组 // var myArray1 [10]int // var myArray2 [10]float32 // myArray3 := [10]int{1, 2, 3, 4} // myArray4 := [4]int{11, 22, 33, 44} // // for i := 0; i < 10; i ++ { // for i := 0; i < len(myArray1); i++ { // fmt.Println(myArray1[i]) // } // fmt.Println("===== ===== ===== =====") // for j := 0; j < len(myArray2); j++ { // fmt.Println(myArray2[j]) // } // fmt.Println("===== ===== ===== =====") // for index, value := range myArray3 { // fmt.Println("index= ", index, ", value = ", value) // } // // 查看数组的数据类型 // fmt.Printf("myArray1 types = %T\n", myArray1) // fmt.Printf("myArray2 types = %T\n", myArray2) // fmt.Printf("myArray3 types = %T\n", myArray3) // fmt.Printf("myArray4 types = %T\n", myArray4) // fmt.Println("===== ===== ===== =====") // printArray(myArray4) // fmt.Println("=====") // for index, value := range myArray4 { // fmt.Println("index = ", index, ", value = ", value) // } // var myArray5 [10]int myArray := []int{1, 2, 3, 4} // 动态数组 切片 slice fmt.Printf("myArray type is %T\n", myArray) printSlice(myArray) fmt.Println("===== ===== ===== =====") // printSlice(myArray5) for _, value := range myArray { fmt.Println("value = ", value) } } `
image
`package main import ( "fmt" ) // func printArray(myArray [4]int) { // // 值拷贝 // for index, value := range myArray { // fmt.Println("index = ", index, " , value = ", value) // } // myArray[0] = 233 // } // func printSlice(myArray []int) { // // 引用传递 // // Slice 和 数组相比的一个好处是可变长度,这为形参的参数值长度提供了更多的可能性,更加灵活了 // // 动态数组是指向数组内存的指针(引用) // for _, value := range myArray { // fmt.Println("value = ", value) // } // myArray[0] = 233 // } func main() { // // 固定长度数组 // var myArray1 [10]int // var myArray2 [10]float32 // myArray3 := [10]int{1, 2, 3, 4} // myArray4 := [4]int{11, 22, 33, 44} // // for i := 0; i < 10; i ++ { // for i := 0; i < len(myArray1); i++ { // fmt.Println(myArray1[i]) // } // fmt.Println("===== ===== ===== =====") // for j := 0; j < len(myArray2); j++ { // fmt.Println(myArray2[j]) // } // fmt.Println("===== ===== ===== =====") // for index, value := range myArray3 { // fmt.Println("index= ", index, ", value = ", value) // } // // 查看数组的数据类型 // fmt.Printf("myArray1 types = %T\n", myArray1) // fmt.Printf("myArray2 types = %T\n", myArray2) // fmt.Printf("myArray3 types = %T\n", myArray3) // fmt.Printf("myArray4 types = %T\n", myArray4) // fmt.Println("===== ===== ===== =====") // printArray(myArray4) // fmt.Println("=====") // for index, value := range myArray4 { // fmt.Println("index = ", index, ", value = ", value) // } // var myArray5 [10]int // myArray := []int{1, 2, 3, 4} // 动态数组 切片 slice // fmt.Printf("myArray type is %T\n", myArray) // printSlice(myArray) // fmt.Println("===== ===== ===== =====") // // printSlice(myArray5) // for _, value := range myArray { // fmt.Println("value = ", value) // } // 声明 slice 的 4 种方法 // 1. 声明 slice1 是一个切片,并且初始化,默认值是 1, 2, 3,长度 len 是 3 slice1 := []int{1, 2, 3} fmt.Printf("len = %d, slice1 = %v\n", len(slice1), slice1) // 2. 声明 slice2 是一个切片,但是此时并未给 slice2 分配内存空间 // 此时无法为 slice2[0] = 1 赋值,会提示越界错误 var slice2 []int // 可以通过 make 开辟内存空间 slice2 = make([]int, 3) slice2[0] = 233 fmt.Printf("len = %d, slice2 = %v\n", len(slice2), slice2) // 3. 声明 slice3 是一个切片,同时为 slice3 分配内存空间,3 个空间,初始化值为 0 var slice3 []int = make([]int, 3) fmt.Printf("len = %d, slice3 = %v\n", len(slice3), slice3) // 4. 声明 slice4 是一个切片,同时为 slice4 分配内存空间,4 个空间,初始化值为 0,通过 := 推导出 slice4 是一个切片 slice4 := make([]int, 4) fmt.Printf("len = %d, slice4 = %v\n", len(slice4), slice4) // 判断 slice 是否为空 var slice5 []int if slice5 == nil { fmt.Println("slice5 is nil,是一个空切片") } else { fmt.Println("slice5 是有空间的") } } `
image
切片的长度(len)和容量(cap)不同,长度表示左指针及右指针之间的距离,容量表示左指针至底层数组末尾的距离。
image
切片的扩容机制,append() 的时候,如果长度增加后超过容量,则将容量增加 2 倍。
`package main import ( "fmt" ) // func printArray(myArray [4]int) { // // 值拷贝 // for index, value := range myArray { // fmt.Println("index = ", index, " , value = ", value) // } // myArray[0] = 233 // } // func printSlice(myArray []int) { // // 引用传递 // // Slice 和 数组相比的一个好处是可变长度,这为形参的参数值长度提供了更多的可能性,更加灵活了 // // 动态数组是指向数组内存的指针(引用) // for _, value := range myArray { // fmt.Println("value = ", value) // } // myArray[0] = 233 // } func main() { // // 固定长度数组 // var myArray1 [10]int // var myArray2 [10]float32 // myArray3 := [10]int{1, 2, 3, 4} // myArray4 := [4]int{11, 22, 33, 44} // // for i := 0; i < 10; i ++ { // for i := 0; i < len(myArray1); i++ { // fmt.Println(myArray1[i]) // } // fmt.Println("===== ===== ===== =====") // for j := 0; j < len(myArray2); j++ { // fmt.Println(myArray2[j]) // } // fmt.Println("===== ===== ===== =====") // for index, value := range myArray3 { // fmt.Println("index= ", index, ", value = ", value) // } // // 查看数组的数据类型 // fmt.Printf("myArray1 types = %T\n", myArray1) // fmt.Printf("myArray2 types = %T\n", myArray2) // fmt.Printf("myArray3 types = %T\n", myArray3) // fmt.Printf("myArray4 types = %T\n", myArray4) // fmt.Println("===== ===== ===== =====") // printArray(myArray4) // fmt.Println("=====") // for index, value := range myArray4 { // fmt.Println("index = ", index, ", value = ", value) // } // var myArray5 [10]int // myArray := []int{1, 2, 3, 4} // 动态数组 切片 slice // fmt.Printf("myArray type is %T\n", myArray) // printSlice(myArray) // fmt.Println("===== ===== ===== =====") // // printSlice(myArray5) // for _, value := range myArray { // fmt.Println("value = ", value) // } // // 声明 slice 的 4 种方法 // // 1. 声明 slice1 是一个切片,并且初始化,默认值是 1, 2, 3,长度 len 是 3 // slice1 := []int{1, 2, 3} // fmt.Printf("len = %d, slice1 = %v\n", len(slice1), slice1) // // 2. 声明 slice2 是一个切片,但是此时并未给 slice2 分配内存空间 // // 此时无法为 slice2[0] = 1 赋值,会提示越界错误 // var slice2 []int // // 可以通过 make 开辟内存空间 // slice2 = make([]int, 3) // slice2[0] = 233 // fmt.Printf("len = %d, slice2 = %v\n", len(slice2), slice2) // // 3. 声明 slice3 是一个切片,同时为 slice3 分配内存空间,3 个空间,初始化值为 0 // var slice3 []int = make([]int, 3) // fmt.Printf("len = %d, slice3 = %v\n", len(slice3), slice3) // // 4. 声明 slice4 是一个切片,同时为 slice4 分配内存空间,4 个空间,初始化值为 0,通过 := 推导出 slice4 是一个切片 // slice4 := make([]int, 4) // fmt.Printf("len = %d, slice4 = %v\n", len(slice4), slice4) // // 判断 slice 是否为空 // var slice5 []int // if slice5 == nil { // fmt.Println("slice5 is nil,是一个空切片") // } else { // fmt.Println("slice5 是有空间的") // } // slice 切片的长度是 3,容量是 5 var slice6 = make([]int, 3, 5) fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // 向 slice6 中添加元素 1,此时 len = 4,cap = 5,[0 0 0 1] slice6 = append(slice6, 1) fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) slice6 = append(slice6, 2) fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // 像一个 slice 容量满了的 slice 中追加元素,Golang 会在底层开辟一定量等长的内存空间,如 原来的容量是 5,则现在的容量为 10 // cap = 10 slice6 = append(slice6, 3) fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) fmt.Println("===== ===== ===== ===== =====") // 如果没指定 cap,则 cap = len var number2 = make([]int, 3) fmt.Printf("len = %d, cap = %d, number2 = %v\n", len(number2), cap(number2), number2) // cap = 6 number2 = append(number2, 111) fmt.Printf("len = %d, cap = %d, number2 = %v\n", len(number2), cap(number2), number2) } `
image
slice 截取后,两个 slice 仍然指向相同的内存空间。可以通过 copy() 函数,将两个 slice 指向不同的内存空间。(深拷贝,拷贝一个副本,将底层数组的 slice 一起进行拷贝。)
`package main import ( "fmt" ) // func printArray(myArray [4]int) { // // 值拷贝 // for index, value := range myArray { // fmt.Println("index = ", index, " , value = ", value) // } // myArray[0] = 233 // } // func printSlice(myArray []int) { // // 引用传递 // // Slice 和 数组相比的一个好处是可变长度,这为形参的参数值长度提供了更多的可能性,更加灵活了 // // 动态数组是指向数组内存的指针(引用) // for _, value := range myArray { // fmt.Println("value = ", value) // } // myArray[0] = 233 // } func main() { // // 固定长度数组 // var myArray1 [10]int // var myArray2 [10]float32 // myArray3 := [10]int{1, 2, 3, 4} // myArray4 := [4]int{11, 22, 33, 44} // // for i := 0; i < 10; i ++ { // for i := 0; i < len(myArray1); i++ { // fmt.Println(myArray1[i]) // } // fmt.Println("===== ===== ===== =====") // for j := 0; j < len(myArray2); j++ { // fmt.Println(myArray2[j]) // } // fmt.Println("===== ===== ===== =====") // for index, value := range myArray3 { // fmt.Println("index= ", index, ", value = ", value) // } // // 查看数组的数据类型 // fmt.Printf("myArray1 types = %T\n", myArray1) // fmt.Printf("myArray2 types = %T\n", myArray2) // fmt.Printf("myArray3 types = %T\n", myArray3) // fmt.Printf("myArray4 types = %T\n", myArray4) // fmt.Println("===== ===== ===== =====") // printArray(myArray4) // fmt.Println("=====") // for index, value := range myArray4 { // fmt.Println("index = ", index, ", value = ", value) // } // var myArray5 [10]int // myArray := []int{1, 2, 3, 4} // 动态数组 切片 slice // fmt.Printf("myArray type is %T\n", myArray) // printSlice(myArray) // fmt.Println("===== ===== ===== =====") // // printSlice(myArray5) // for _, value := range myArray { // fmt.Println("value = ", value) // } // // 声明 slice 的 4 种方法 // // 1. 声明 slice1 是一个切片,并且初始化,默认值是 1, 2, 3,长度 len 是 3 // slice1 := []int{1, 2, 3} // fmt.Printf("len = %d, slice1 = %v\n", len(slice1), slice1) // // 2. 声明 slice2 是一个切片,但是此时并未给 slice2 分配内存空间 // // 此时无法为 slice2[0] = 1 赋值,会提示越界错误 // var slice2 []int // // 可以通过 make 开辟内存空间 // slice2 = make([]int, 3) // slice2[0] = 233 // fmt.Printf("len = %d, slice2 = %v\n", len(slice2), slice2) // // 3. 声明 slice3 是一个切片,同时为 slice3 分配内存空间,3 个空间,初始化值为 0 // var slice3 []int = make([]int, 3) // fmt.Printf("len = %d, slice3 = %v\n", len(slice3), slice3) // // 4. 声明 slice4 是一个切片,同时为 slice4 分配内存空间,4 个空间,初始化值为 0,通过 := 推导出 slice4 是一个切片 // slice4 := make([]int, 4) // fmt.Printf("len = %d, slice4 = %v\n", len(slice4), slice4) // // 判断 slice 是否为空 // var slice5 []int // if slice5 == nil { // fmt.Println("slice5 is nil,是一个空切片") // } else { // fmt.Println("slice5 是有空间的") // } // // slice 切片的长度是 3,容量是 5 // var slice6 = make([]int, 3, 5) // fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // // 向 slice6 中添加元素 1,此时 len = 4,cap = 5,[0 0 0 1] // slice6 = append(slice6, 1) // fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // slice6 = append(slice6, 2) // fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // // 像一个 slice 容量满了的 slice 中追加元素,Golang 会在底层开辟一定量等长的内存空间,如 原来的容量是 5,则现在的容量为 10 // // cap = 10 // slice6 = append(slice6, 3) // fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // fmt.Println("===== ===== ===== ===== =====") // // 如果没指定 cap,则 cap = len // var number2 = make([]int, 3) // fmt.Printf("len = %d, cap = %d, number2 = %v\n", len(number2), cap(number2), number2) // // cap = 6 // number2 = append(number2, 111) // fmt.Printf("len = %d, cap = %d, number2 = %v\n", len(number2), cap(number2), number2) // len = 3, cap = 3 s := []int{111, 222, 333} // 左闭右开 [0, 1) s1 := s[0:1] fmt.Printf("s1 = %v \n", s1) // [0, 2) [111, 222] s2 := s[0:2] fmt.Printf("s2 = %v \n", s2) // 表示截取全部元素 s3 := s[:] fmt.Printf("s3 = %v \n", s3) // 默认下限为 0, [111 222] s4 := s[:2] fmt.Printf("s4 = %v \n", s4) // 默认上限为 len() [222 333] s5 := s[1:] fmt.Printf("s5 = %v \n", s5) // slice 截取后,两个 slice 仍然指向相同的内存空间 s1[0] = 888 fmt.Printf("new s = %v \n", s) fmt.Println("===== ===== ===== ===== =====") // c2 = [0 0 0] c2 := make([]int, 3) // 将 s 中的值依次拷贝值 c2 中 // 此时 c2 与 s 是完全不同的内存空间(新内存空间) copy(c2, s) fmt.Printf("c2 = %v \n", c2) } `
image
可以,此时新截取的变量类型为 slice。
`package main import ( "fmt" ) // func printArray(myArray [4]int) { // // 值拷贝 // for index, value := range myArray { // fmt.Println("index = ", index, " , value = ", value) // } // myArray[0] = 233 // } // func printSlice(myArray []int) { // // 引用传递 // // Slice 和 数组相比的一个好处是可变长度,这为形参的参数值长度提供了更多的可能性,更加灵活了 // // 动态数组是指向数组内存的指针(引用) // for _, value := range myArray { // fmt.Println("value = ", value) // } // myArray[0] = 233 // } func main() { // // 固定长度数组 // var myArray1 [10]int // var myArray2 [10]float32 // myArray3 := [10]int{1, 2, 3, 4} // myArray4 := [4]int{11, 22, 33, 44} // // for i := 0; i < 10; i ++ { // for i := 0; i < len(myArray1); i++ { // fmt.Println(myArray1[i]) // } // fmt.Println("===== ===== ===== =====") // for j := 0; j < len(myArray2); j++ { // fmt.Println(myArray2[j]) // } // fmt.Println("===== ===== ===== =====") // for index, value := range myArray3 { // fmt.Println("index= ", index, ", value = ", value) // } // // 查看数组的数据类型 // fmt.Printf("myArray1 types = %T\n", myArray1) // fmt.Printf("myArray2 types = %T\n", myArray2) // fmt.Printf("myArray3 types = %T\n", myArray3) // fmt.Printf("myArray4 types = %T\n", myArray4) // fmt.Println("===== ===== ===== =====") // printArray(myArray4) // fmt.Println("=====") // for index, value := range myArray4 { // fmt.Println("index = ", index, ", value = ", value) // } // var myArray5 [10]int // myArray := []int{1, 2, 3, 4} // 动态数组 切片 slice // fmt.Printf("myArray type is %T\n", myArray) // printSlice(myArray) // fmt.Println("===== ===== ===== =====") // // printSlice(myArray5) // for _, value := range myArray { // fmt.Println("value = ", value) // } // // 声明 slice 的 4 种方法 // // 1. 声明 slice1 是一个切片,并且初始化,默认值是 1, 2, 3,长度 len 是 3 // slice1 := []int{1, 2, 3} // fmt.Printf("len = %d, slice1 = %v\n", len(slice1), slice1) // // 2. 声明 slice2 是一个切片,但是此时并未给 slice2 分配内存空间 // // 此时无法为 slice2[0] = 1 赋值,会提示越界错误 // var slice2 []int // // 可以通过 make 开辟内存空间 // slice2 = make([]int, 3) // slice2[0] = 233 // fmt.Printf("len = %d, slice2 = %v\n", len(slice2), slice2) // // 3. 声明 slice3 是一个切片,同时为 slice3 分配内存空间,3 个空间,初始化值为 0 // var slice3 []int = make([]int, 3) // fmt.Printf("len = %d, slice3 = %v\n", len(slice3), slice3) // // 4. 声明 slice4 是一个切片,同时为 slice4 分配内存空间,4 个空间,初始化值为 0,通过 := 推导出 slice4 是一个切片 // slice4 := make([]int, 4) // fmt.Printf("len = %d, slice4 = %v\n", len(slice4), slice4) // // 判断 slice 是否为空 // var slice5 []int // if slice5 == nil { // fmt.Println("slice5 is nil,是一个空切片") // } else { // fmt.Println("slice5 是有空间的") // } // // slice 切片的长度是 3,容量是 5 // var slice6 = make([]int, 3, 5) // fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // // 向 slice6 中添加元素 1,此时 len = 4,cap = 5,[0 0 0 1] // slice6 = append(slice6, 1) // fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // slice6 = append(slice6, 2) // fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // // 像一个 slice 容量满了的 slice 中追加元素,Golang 会在底层开辟一定量等长的内存空间,如 原来的容量是 5,则现在的容量为 10 // // cap = 10 // slice6 = append(slice6, 3) // fmt.Printf("len = %d, cap = %d, slice6 = %v\n", len(slice6), cap(slice6), slice6) // fmt.Println("===== ===== ===== ===== =====") // // 如果没指定 cap,则 cap = len // var number2 = make([]int, 3) // fmt.Printf("len = %d, cap = %d, number2 = %v\n", len(number2), cap(number2), number2) // // cap = 6 // number2 = append(number2, 111) // fmt.Printf("len = %d, cap = %d, number2 = %v\n", len(number2), cap(number2), number2) // // len = 3, cap = 3 // s := []int{111, 222, 333} // // 左闭右开 [0, 1) // s1 := s[0:1] // fmt.Printf("s1 = %v \n", s1) // // [0, 2) [111, 222] // s2 := s[0:2] // fmt.Printf("s2 = %v \n", s2) // // 表示截取全部元素 // s3 := s[:] // fmt.Printf("s3 = %v \n", s3) // // 默认下限为 0, [111 222] // s4 := s[:2] // fmt.Printf("s4 = %v \n", s4) // // 默认上限为 len() [222 333] // s5 := s[1:] // fmt.Printf("s5 = %v \n", s5) // // slice 截取后,两个 slice 仍然指向相同的内存空间 // s1[0] = 888 // fmt.Printf("new s = %v \n", s) // fmt.Println("===== ===== ===== ===== =====") // // c2 = [0 0 0] // c2 := make([]int, 3) // // 将 s 中的值依次拷贝值 c2 中 // // 此时 c2 与 s 是完全不同的内存空间(新内存空间) // copy(c2, s) // fmt.Printf("c2 = %v \n", c2) var arr1 [5]int arr1 = [5]int{111, 222, 333, 444, 555} fmt.Printf("arr1 = %v \n", arr1) arr2 := arr1[2:4] fmt.Printf("arr2 = %v \n", arr2) fmt.Printf("arr1 type = %T \n", arr1) fmt.Printf("arr2 type = %T \n", arr2) } `
image