数组:
*go中数组是值类型,数组作为参数时是进行的值拷贝
数组创建方式:
package main
import "fmt"
func main() {
//数组定义方式
//数组的长度必须为数值常量
var arr [3]int
//var i int = 0
//var ai [i]int
arr[0] = 1
arr[1] = 10
arr[2] = 22
fmt.Println(arr)
var arr2 = [3]int{2, 4, 6}
fmt.Println(arr2)
//有编译器自动识别数组长度
var arr3 = [...]int{5, 7, 8}
fmt.Println(arr3)
//可以定义空数组,但无实际价值
arr4 := [...]int{}
fmt.Println("arr4 len:", len(arr4))
//arr4[0] = 0 //编译报错,下标越界
//指定下标和对应的值
arr5 := [3]int{0:9, 1:8, 2:7}
fmt.Println("arr5 ", arr5)
arr6 := [...]int{4:101}
fmt.Println("arr6", arr6)
//new 为内建函数 返回值为对应指针
arr7Point := new([3]int)
fmt.Printf("arr7Point type %T \n", arr7Point)
(*arr7Point)[0] = 256
(*arr7Point)[1] = 26
(*arr7Point)[2] = 356
fmt.Printf("*arr7Poiont %v ; len= %v \n", *arr7Point, len(*arr7Point))
}
*数组的指针指向的是数组第一个元素的地址,即数组的指针==数组第一个元素的指针
*go中同数据类型元素的不同长度的数组认为非同类型数组
*通过for 或 for - range遍历数组
package main
import (
"fmt"
)
func main() {
arr1 := [4]int{1, 2, 3, 4}
fmt.Printf("arr1 address -- %p , arr1[0] address -- %p \n", &arr1, &arr1[0])
//通过指针可以计算对应元素的位置
//64位os,int为64位即8个字节,数组内存是连续的,
//arr1[0]的地址加8字节即为arr[1]的位置
fmt.Printf("arr1[0] address -- %p, arr1[1] address -- %p \n", &arr1[0], &arr1[1])
arr2 := [...]int{1, 2}
foreachLen4(arr1)
fmt.Println(arr1)
foreachLen2(arr2)
//foreachLen2(arr1) //编译报错
//foreachLen4(arr2) //编译报错
}
func foreachLen4(arr [4]int) {
for i := 0; i < len(arr); i++ {
fmt.Println("foreachLen4 - ", arr[i])
//go中数组为值类型 进行值拷贝
arr[i] = arr[i] + 1
}
}
func foreachLen2(arr [2]int) {
//range 可将数组下标和对应的值返回, 通过下划线 可以忽略 下标或值
// for _, val := range arr
// for idx, _ := range arr
for idx, val := range arr {
fmt.Printf("foreachLen2: idx -- %v, val -- %v \n", idx, val)
}
}
切片slice
*go中特有的数据类型切片类似可变数组,切片持有对应数组的元素指针
package main
import "fmt"
func main() {
//指针的创建
var arr [5]int = [5]int{1, 2, 3, 4, 5}
//从数组映射 数组[起始下标 : 截止下标], 含头不含尾
//注意[]int中“[]”无数字
var slice []int= arr[1:3]
fmt.Println("slice ", slice)
//当起始下标为0 ,或截止下标为对应数组长度时可省略
slice2 := arr[:3]
slice3 := arr[1:]
slice4 := arr[:]
fmt.Println("slice2: ", slice2, " slice3: ", slice3, " slice4:", slice4)
//切片中的元素对应映射数组元素的指针
fmt.Printf("arr addr -- %p, arr[1] addr -- %p , arr[2] addr -- %p \n",
&arr, &arr[1], &arr[2])
fmt.Printf("slice addr -- %p, slice[0] addr -- %p, slice[1] addr -- %p \n ",
&slice[0], &slice[1])
//make 为内建函数,创建应用类型
//make(切片类型, 初始长度, 初始容量)
slice5 := make([]int, 3, 6)
slice5[0] = 99
slice5[1] = 88
slice5[2] = 77
//切片有长度 与 容量属性,容量随元素个数的增加而自动扩充,内建函数cap可获得容量
fmt.Printf("slice5 len %v , cap %v", len(slice5), cap(slice5))
fmt.Println("slice5", slice5)
//切片虽为可变长,但增加超过初始长度的元素时不可通过下标直接赋值,需使用内建函数append
// slice5[3] = 66 //编译报错 越界
//append 会返回扩充后的切片
slice5 = append(slice5, 66)
fmt.Println("slice5 --", slice5)
//可以append对应类型切片,但不可append数组 ==》类型不匹配
slice5 = append(slice5, slice...)
// slice5 = append(slice5, arr...) //编译报错
fmt.Println("slice5 ** ", slice5)
}
切片扩容
package main
import (
"math/rand"
"time"
"fmt"
)
func main() {
slice := make([]int, 5, 10)
//slice 是引用类型
generate(slice)
fmt.Println("slice ", slice)
arr := [5]int{1, 2, 3, 4, 5}
slice2 := arr[:]
fmt.Println("slice2: ", slice2, "arr ", arr)
fmt.Println("slice2 addr: ", &slice2, "arr addr:", &arr)
generate(slice2)
//切片是数组元素的引用,故切片改变会导致原数组改变,反过来也是
//虽然元素对应的值发生变化,但元素对应的指针并未发生变化
//(开辟的空间可以存放对应的数据类型)
//切片可以理解成是由 对应数组元素的指针组成的数组 + 长度属性 + 容量属性 的结构
fmt.Println("slice2 -- ", slice2, "arr -- ", arr)
fmt.Println("slice2 --addr ", &slice2, "arr --addr ", &arr)
fmt.Printf("arr len %v , slice2 len %v \n",
len(arr), len(slice2))
//append带来的改变
slice3 := append(slice2, 6)
fmt.Printf("arr add - %p, slice2 add - %p, slice3 add - %p \n",
&arr, &slice2, &slice3)
//append 是将原有切片对应的元素的值拷贝到新的地址,故而新切片元素对应的指针发生改变
//append是重新分配内存空间
fmt.Printf("arr len %v , slice2 len %v, slice3 len %v \n",
len(arr), len(slice2), len(slice3))
fmt.Printf("arr[1] add %p, slice2[1] add %p, slice3[1] add %p \n",
&arr[1], &slice2[1], &slice3[1])
}
func generate(slice []int) {
//设置随机因子
rand.Seed(time.Now().UnixNano())
for i := 0; i < 5; i++ {
slice[i] = rand.Intn(100)
}
}
切片append与切片容量
*一个大坑:
*通过数组映射过来的切片长度与容量一致,make声明切片时可省略容量此时默认容量与长度一致;
*切片append所返回的新切片会共用原切片所定义的容量范围内的空间,即在append的元素不超过设定的容量时,新的切片元素的指针与原有切片元素的指针 指向的是同一个地址,当超出容量时则指向不同的地址
package main
import (
"fmt"
)
func main() {
//容量
arr := [3]int{1, 2, 3}
s := arr[:]
fmt.Println("s = ", s, " len ", len(s), " cap ", cap(s))
s1 := make([]int, 3)
fmt.Println("s1 = ", s1, " len ", len(s1), " cap ", cap(s1))
fmt.Println("-----------------------------")
//
slice := make([]int, 2, 3)
slice[0] = 8
slice[1] = 88
fmt.Printf("slice地址:%p slice[0]地址:%p slice[1]地址:%p \n", &slice, &slice[0], &slice[1])
fmt.Println("-----------slice append 9 ==> slice2------------------")
slice2 := append(slice, 9)
fmt.Println("slice = ", slice, " len ", len(slice), " cap ", cap(slice))
fmt.Printf("slice地址:%p slice[0]地址:%p slice[1]地址:%p \n", &slice, &slice[0], &slice[1])
fmt.Println("slice2 = ", slice2, " len ", len(slice2), " cap ", cap(slice2))
fmt.Printf("slice2地址:%p slice2[0]地址:%p slice2[1]地址:%p slice2[1]地址:%p \n",
&slice2, &slice2[0], &slice2[1], &slice2[2])
//slice2 append超过 slice 的容量时 slice2元素的地址就与slice不一致
// fmt.Println("-----------slice2 append ==> slice2------------------")
// slice2 = append(slice2, 99)
// fmt.Println("slice = ", slice)
// fmt.Printf("slice地址:%p slice[0]地址:%p slice[1]地址:%p \n", &slice, &slice[0], &slice[1])
// fmt.Println("slice2 = ", slice2)
// fmt.Printf("slice2地址:%p slice2[0]地址:%p slice2[1]地址:%p slice2[2]地址:%p slice2[3]地址:%p\n",
// &slice2, &slice2[0], &slice2[1], &slice2[2], &slice2[3])
fmt.Println("-----------slice append 100 ==> slice------------------")
slice = append(slice, 100)
fmt.Println("slice = ", slice, " len ", len(slice), " cap ", cap(slice))
fmt.Printf("slice地址:%p slice[0]地址:%p slice[1]地址:%p slice[2]地址:%p \n",
&slice, &slice[0], &slice[1], &slice[2])
fmt.Println("slice2 = ", slice2, " len ", len(slice2), " cap ", cap(slice2))
fmt.Printf("slice2地址:%p slice2[0]地址:%p slice2[1]地址:%p slice2[2]地址:%p \n",
&slice2, &slice2[0], &slice2[1], &slice2[2])
fmt.Println("-----------slice append 20 ==> slice-----超过容量-------------")
slice = append(slice, 20)
fmt.Println("slice = ", slice, " len ", len(slice), " cap ", cap(slice))
fmt.Printf("slice地址:%p slice[0]地址:%p slice[1]地址:%p slice[2]地址:%p slice[3]地址:%p \n",
&slice, &slice[0], &slice[1], &slice[2], &slice[3])
fmt.Println("slice2 = ", slice2, " len ", len(slice2), " cap ", cap(slice2))
fmt.Printf("slice2地址:%p slice2[0]地址:%p slice2[1]地址:%p slice2[2]地址:%p \n",
&slice2, &slice2[0], &slice2[1], &slice2[2])
}
string 与切片以及rune
package main
import (
"fmt"
)
func main() {
//string 底层是byte数组,且该数组不可变
str := "hello,独角兽!"
fmt.Println(str)
strSlice := str[:5]
fmt.Println("截取:", strSlice)
//因string对应的byte数组不可变,故而str对应的切片中的元素也不可以重新赋值
// strSlice[0] = 'A'
//通过byte数组来改变
bytes := []byte(str)
bytes[0] = 'A'
newStr := string(bytes)
fmt.Println("str -- ", str, " newStr --", newStr)
//因go为utf8编码,byte对应一个字节,汉字为三个字节,无法放入单个字节
//rune的定义:32位有符号整形,int32的别名;将string转换成rune数组时
//解决了汉字长度的问题
// bytes[0] = '我'
//rune 按照字符处理string
strRune := []rune(str)
strRune[0] = '我'
newStr = string(strRune)
fmt.Println(newStr)
}