golang学习笔记——数组与切片

数组:

*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)

}

 

 

 

 

 

 

 

你可能感兴趣的:(golang)