go的一些知识点

func TestDefer(t *testing.T) {
	defer func() { fmt.Println("1")}()
	defer func() { fmt.Println("2")}()
	defer func() { fmt.Println("3")}()

	panic("error")
}
func f(n int) (r int)  {
	defer func() {
		fmt.Println("anc")
		r += n
		recover()
	}()

	defer func() {
		fmt.Println("temp")
	}()
	var f func()
	defer f()   // 这里 f() 没有被定义,发生错误,会继续调用之前的defer
	f = func() {
		r += 2
	}
	return n + 1  // 相当于 r = n + 1
}

func main() {
    fmt.Println(f(3))
}
// 输出:
// temp
// anc
// 7


当协程碰到 panic 的时候,会遍历该协程的 defer 并执行,如果在 defer 执行的过程遇到 recover 则停止 panic ,返回 recover 继续向下执行,否则遍历完 defer,然后抛出 panic 信息

func TestForRange(t *testing.T) {
	slice := []int{0, 1}

	for i := 0; i < len(slice); i++ {
		fmt.Println(&slice[i])
	}

	for _, v := range slice {
		fmt.Println(&v)
	}
}

for range 循环的时候会创建每个元素的副本,而不是元素的引用

new、make 都是用来分配内存,但是适用的类型不同

new(T) 会为 T 类型的新值分配已置零的内存空间,并返回地址,适用于值类型

make(T) 会返回初始化之后的 T 类型的值类型。只适合 slice,map,channel

func TestNew(t *testing.T) {
	list := new([]int)
	list = append(list, 1)
}

new([]int) 之后的list是一个 *[]int 类型的指针,不能对指针执行 append 操作

func TestAppendSlice(t *testing.T) {
	s1 := []int{1, 2, 3}
	s2 := []int{4, 5}
	s1 = append(s1, s2)
	fmt.Println(s1, s2)
}

append() 第二个参数不能直接使用 slice , 需要使用 ... 操作符

func TestStructEqual(t *testing.T) {
	stu1 := struct {
		age int
		name string
	}{age: 10, name: "小明"}

	stu2 := struct {
		age int
		name string
	}{age: 10, name: "小明"}

	fmt.Println(stu1 == stu2)

	stu3 := struct {
		language map[string]string
	}{language: map[string]string{"go": "verygood", "php": "bad"}}
	stu4 := struct {
		language map[string]string
	}{language: map[string]string{"go": "verygood", "php": "bad"}}
	
	fmt.Println(stu3 == stu4) // 这里会报错
}

结构体只能比较是否相等,不能比较大小

相同类型的结构体才能比较,结构体是否相同不仅仅与属性相关,还与属性的顺序有关

如果结构体内的成员可以比较,则该结构体才能进行比较

type int1 int
type int2 = int

var i int = 0
var i1 int1 = i
var i2 int2 = i
fmt.Println(i1, i2)

int1 是基于 int 类型创建的,int2 是创建了 int 类型的别名

func TestRun(t *testing.T) {
	a := []int{7, 8, 9}
	fmt.Printf("%+v\n", a)    // [7 8 9]
	ap(a)
	fmt.Printf("%+v\n", a)		// [7 8 9]
	aap(a)
	fmt.Printf("%+v\n", a)		// [1 8 9]
}
func ap(a []int) {
	a = append(a, 10)
	fmt.Printf("%+v\n", a)		// [7 8 9 10]
}
func aap(a []int) {
	a[0] = 1
}

append 会导致低层数组被重新分配,所以原切片还是原切片

const (
	x = iota
	_
  y
	z = "zz"
	k
	p = iota
)
func TestIota(t *testing.T) {
	fmt.Println(x, z, k, p) // 0 2 zz zz 5
}
  • iota 只能在常量表达式中使用
  • 每次 const 出现时,都会让 iota 初始化为 0
  • const中每新增一行常量声明将使 iota 计数一次
  • 可以使用自定义枚举类型
  • 可以使用下划线 _ 跳过不想要的值
  • 可以使用位掩码表示 x = 1 << iota
  • 太多了,自己去网上了解吧
var x = nil	// error
var y interface{} = nil
var z string = nil  // error
var k error = nil

nil 值只能赋值给指针、chan、func、interface、map、slice

func init() {}

  • https://juejin.im/post/6844903864555012104

  • init() 函数是用于程序执行前进行包的初始化的函数,初始化变量等

  • 一个包可以出现多个init() 函数,一个源文件也可以出现多个

  • 同一个包中多个 init() 函数的执行顺序没有明确定义,但是不同包的 init() 函数是根据包导入的依赖关系决定的

  • init() 函数在代码中不能被显式调用、不能被引用,否则会出现变异错误

  • 一个包被引用多次 A import B、C import B、A import C B 被引用多次,但 B 只会初始化一次

  • 引入包不能出现死循环 A import B、 B import A

	if nil == func() int { return 1 } {
		fmt.Println("ok")
	}else {
		fmt.Println("不相等")  // 输出
	}

这里是 函数跟 nil 比

	i := func() int { return 1 }
	switch i.(type) {  // 错误
	case int:
		fmt.Println("int")
	case string:
		fmt.Println("string")
	case interface{}:
		fmt.Println("interface")
	default:
		fmt.Println("不知道类型")
	}

只有接口类型才可以使用类型选择

fmt.Println(x, y, z, k, p)
var m map[person]int
p := person{"tom"}
fmt.Println(m[p])  // 0

这里输出为 0,打印map中不存在的值时,返回元素类型的零值

hello := func(num ...int) { num[0] = 18 }

i := []int{5, 6, 7}
hello(i...)  // 可变参数传入切片的时候需要用到  ... 
fmt.Println(i)  // [18, 6, 7]
	a := 1
	b := 1.1
	fmt.Println(a + b)

编译错误,不同类型不能相加

type coder interface {
	code()
	debug()
}

type Gopher struct {
	language string
}

func (p Gopher) code()  {
	fmt.Printf("I am coding %s language\n", p.language)
}

func (p *Gopher) debug()  {
	fmt.Printf("I am debuging %s language\n", p.language)
}
func TestInterface3(t *testing.T) {
	var c coder = &Gopher{"Go"}  // 这里必须使用指针类型
	c.code()
	c.debug()

	var d coder
	fmt.Println(d == nil)	// true
	fmt.Printf("c: %T, %v\n", d, d) // nil, nil

	var e *Gopher
	fmt.Println(e == nil) // true

	d = e
	fmt.Println(d == nil)	// false
	fmt.Printf("c: %T, %v\n", d, d)	// *main.Gopher, nil
}

接口值的零值是指动态类型动态值都为 nil。当仅且当这两部分的值都为 nil 的情况下,这个接口值就才会被认为 接口值 == nil

type MyError struct {}

func (i MyError) Error() string {
	return "MyError"
}
func TestInterface4(t *testing.T)  {
	err := Process()
	fmt.Println(err)	// nil
	fmt.Println(err == nil)  // false
	fmt.Printf("err %T, %v\n", err, err) // *main.MyError, nil
	fmt.Printf("nil %T, %v\n", nil, nil) // nil, nil
}
func Process() error {
	var err *MyError = nil
	return err
}
a := [5]int{1,2, 3, 4, 5}
t := a[3:4:5]
fmt.Println(t) // 4

操作符 [i, j] 从索引 i 到索引 j 结束,第三个参数为新切片容量,但是不能超过原数组(切片)底层数组的大小

计算:[i:j:k]

  • 长度: j - i
  • 容量: k - i

cap 适用于 array, slice, channel

s := make(map[string]int)
delete(s, "name")	// 删除不存在的键值对时不会报错
fmt.Println(s["name"]) // 0 (零值)
func hello (i int) {
	fmt.Println(i) // 5
}
func TestRun3(t *testing.T) {
	i := 5
 	defer hello(i)
	i = i + 5
}

defer 的时候会保存一份副本

	str := "string"
	str[0] = 'x' // 报错
	fmt.Println(str)

go 中字符串是只读的

	var s1 []int	// is nil
	var s2 = []int{} // not nil
	if s1 == nil {
		fmt.Println("is nil")
	} else {
		fmt.Println("not nil")
	}

nil 切片和空切片

	s := [3]int{1, 2, 3}
	b := s[:0]
	c := s[:2]
	d := s[1:2:cap(s)]

	fmt.Println(s, len(s), cap(s)) // [1 2 3] 3 3
	fmt.Println(b, len(b), cap(b)) // [] 0, 3
	fmt.Println(c, len(c), cap(c)) // [1 2] 2 3
	fmt.Println(d, len(d), cap(d)) // [2] 1 2

s[1:2] 为 左闭右开 区间 [1, 2) 。如果没有指定第三个参数,则新切片的cap与原切片相等

type Person struct {
    age int
}

func main() {
    person := &Person{28}

    defer fmt.Println(person.age) // 28

    defer func(p *Person) {
        fmt.Println(p.age)  // 29
    }(person)  

    defer func() {
        fmt.Println(person.age)  // 29
    }()

    person.age = 29
}

// 输出 29 29 28

person.age 相当于是把28当做 defer函数的参数,放到栈中

永远不要使用一个指针指向一个接口类型,因为它已经是一个指针。

	var x string = nil
	if x == nil {
		x = "default"
	}
	fmt.Println(x)

golang的字符串类型不能赋值为 nil ,也不能跟 nil 比较

var a bool = true
func main() {
    defer func(){
        fmt.Println("1")
    }()
    if a == true {
        fmt.Println("2")
        return
    }
    defer func(){
        fmt.Println("3")
    }()
}
	s1 := []int{1, 2, 3}
	s2 := s1[1:]
	s2[1] = 4
	fmt.Println(s1, s2) // [1, 2, 4] [2, 4]
	s2 = append(s2, 5, 6 ,7) // [1, 2, 4] [2, 4, 5, 6, 7]
	fmt.Println(s1, s2)

当使用了 s[1:] 获得切片 s2, 此时 s2 会和 s1 共享一个底层数组

append 操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1

	if a := 1; false {
	}else if b := 2; false {
	} else {
		fmt.Println(a, b) // 1 2
	}
    m := map[int]string{0:"zero",1:"one"}
    for k,v := range m {
        fmt.Println(k,v)
    }
// 输出  0 zero 1 one  或者  1 one  0 zero

map 的输出是无序的

func main() {
    a := 1
    b := 2
    defer calc("1", a, calc("10", a, b))
    a = 0
    defer calc("2", a, calc("20", a, b))
    b = 1
}

func calc(index string, a, b int) int {
    ret := a + b
    fmt.Println(index, a, b, ret)
    return ret
}

当程序执行到 defer calc("1", a, calc("10", a, b)) 的时候,会先执行 calc() 函数

defer 定义的函数是延迟函数,故 calc("1",1,3) 会被延迟执行;

下同

type People interface {
	Show()
}

type Student struct {}
func (stu *Student) Show() {}
func main() {
  var s *Student
	if s == nil {
		fmt.Println("s is nil") // 输出
	}else{
		fmt.Println("s is no tnil")
	}

	var p People = s
	if p == nil {
		fmt.Println("p is  nil")
	}else {
		fmt.Println("p is not nil") // 输出
	}
}
var p2 *int

func foo () (*int, error) {
	var i int = 5
	return &i, nil
}
func bar() {
	fmt.Println(*p2)
}

func main() {
	p2, err := foo()
	if err != nil {
		fmt.Println(err)
		return
	}
	bar()
	fmt.Println(*p2)
}

对于使用了 := 的变量,如果新变量与同名已定义的变量不在同一个作用域中,那么Go会新定义这个变量。修改成如下就好了

var err error
p2, err = foo()
bar()
fmt.Println(p2)
	a, b := 1.1 , 3
	fmt.Printf("%T\n", a) // float64

	a, d := 4, 5
	fmt.Printf("%T\n", a) // float64
	a, b := "a" , 3
	fmt.Printf("%T\n", a) // float64

	a, d := 4, 5     // 编译错误
	fmt.Printf("%T\n", a)
	var m = [...]int{1, 2, 3}
	for i, v := range  m {
		go func() {
			fmt.Println(i, v)
		}()
	}
	time.Sleep(time.Second * 3)
// 输出  2 3  2 3  2 3

变量 i,v 在每次循环体中都会被重用,而不是重新声明

各个goroutine中输出的都是循环结束的 i , v

可以使用如下两种方法

for i, v := range m {
  go func(i, v int) {
    fmt.Println(i, v)
  }(i, v)
}

for i, v := range m {
  i, v := i, v
  go func() {
    fmt.Println(i, v)
  }
}

当然这里的输出也不是顺序输出

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值