切片和数组的区别

1:切片是引用类型,数组是值类型

2:切片可以用make关键字创建

package main

import "fmt"

func main() {

   slice_4 := make([]int, 5, 9)
   // len=5, cap=9, slice=[0 0 0 0 0]
   fmt.Printf("len=%d, cap=%d, slice=%v\n", len(slice_4), cap(slice_4), slice_4) 

}

3:数组的大小必须在创建时就声明,切片不需要,切片会在容量不够时自动扩容,每次扩容是当前容量的2倍

a := [5]int{1, 2, 3, 4, 5}  // 声明一个包含5个整数的数组
b := []int{1, 2, 3, 4, 5}  // 声明一个包含5个整数的切片

// 个人理解就是在写[]int{}时,如果[]里写了数字,那就是数组,不写就是切片


c := [...]int{1, 2, 3, 4, 5}
// 这种写法看似也没有声明长度,其实编译器编译后等同于 =》 c := [5]int{1, 2, 3, 4, 5}
// 所以这种还是数组,值传递

指针类型,引用类型,值类型

  • 值类型:基本数据类型和结构体都是值类型。变量的值直接存储在变量本身的内存空间中,而不是通过指针或引用来访问。在函数中传递时,实际传递的是值的副本,因此无法直接修改值类型本身,如果想要在函数中修改值类型,则需要获取到值类型的指针。示例代码,add就是值传递,add2就是指针传递
package main

import "fmt"

func main() {
	// 定义一个int类型变量 num
	var num = 1
	fmt.Println("num = ", num) // num =  1
	add(num)
	fmt.Println("add之后,num = ", num) // add之后,num =  1
	add2(&num)
	fmt.Println("add2之后,num = ", num) // add之后,num =  2
}

func add(num int) {
	num++
}

func add2(num *int) {
	*num++
}

  • 指针类型:它保存了一个变量在内存中的地址,可以通过指针去访问变量的值,或者修改变量的值。通常是值类型会转成指针类型去直接操作值类型变量的值。

  • 引用类型:引用类型在内部使用指针来实现,它的值是一个指向底层数据结构的指针。因此再进行赋值或者函数传递时,实际上是传递了一个指向底层数据结构的指针,因此可以直接来操作数据。

不同协程之间是无法捕获彼异常的

package main

import (
   "fmt"
   "time"
)

func GoA() {
   defer func() {
      if err := recover(); err != nil {
         fmt.Println("GoA panic:" + fmt.Sprintf("%s", err))
      }
   }()

   go GoB()
}

func GoB() {
   panic("GoB:error")
}

func main() {
   go GoA()
   time.Sleep(1 * time.Second)
   fmt.Println("main")
}

打印:

panic: GoB:error

GoA函数中创建了一个协程GoB,并且在GoB协程中发生了异常,那么GoA函数中的deferrecover是无法捕获这个异常的,因为GoB协程是在另外一个独立的调用栈中运行的。