go语言需要注意点

Panic 和 Fatal #

Fatal #

执行log.Fatal程序直接退出,不会执行其它任何defer

#源码
func Fatal(v ...interface{}) {
	std.Output(2, fmt.Sprint(v...))
	os.Exit(1)
}

//说明:直接屏幕输出,退出程序直接exit了


#测试代码
func main()  {
	fmt.Println("main 1111")
	defer fmt.Println("main defer 22222")
	test1()

}

func test1()  {
	fmt.Println("test 1111")
	defer fmt.Println("test derfer 22222")
	log.Fatal("test 3333333")
}

#output
main 1111
test 1111
2022/03/07 23:37:09 test 3333333

panic #

类似其它语言的异常exception,执行后会执行defer,同时也可以通过recover捕获唤醒

func main()  {
	fmt.Println("main 1111")
	defer func() {
		fmt.Println("main defer 22222")
		if err := recover(); err != nil {
			fmt.Println("main recover",err)
		}
	}()
	test1()

}

func test1()  {
	fmt.Println("test 1111")
	defer func() {
		fmt.Println("test derfer 22222")
		//if err := recover(); err != nil {
		//	fmt.Println("test recover",err)
		//}

	}()
	log.Panic("test 3333333")
}

#output
1main 1111
2test 1111
32022/03/07 23:41:48 test 3333333
4test derfer 22222
#如果test中recover去掉注释会执行5不会执行7
5test recover test 3333333
6main defer 22222
#注释掉test中recover代码不执行5执行7
7main recover test 3333333

new和make #

new #

我们一般通过var声明变量,变量未初始化,这些变量未分配内存地址

var i int
var s string
var i1 *int

下面代码会报错

func main() {
 var i *int
 *i=10
 fmt.Println(*i)
}

panic: runtime error: invalid memory address or nil pointer dereference

于引用类型的变量,我们不光要声明它,还要为它分配内容空间,否则我们的值放在哪里去呢?所以指针、切片、数组、通道(chan)都需要初始化并分配内存地址

func main() {
 var i *int
 i=new(int)
 *i=10
 fmt.Println(*i)
}

#output
10
type user struct {
	Name string
	Lock sync.Mutex
}

func main()  {
	u := new(user)
	fmt.Printf("%+v",u)
}

#output
&{Name: Lock:{state:0 sema:0}}

不用初始化,自动分配各类型的默认值

new它返回的永远是类型的指针,指向分配类型的内存地址

make #

make也是用于内存分配的,但是和new不同,它只用于chan、map以及切片的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。

func make(t Type, size …IntegerType) Type

从函数声明中可以看到,返回的还是该类型。

二者异同

所以从这里可以看的很明白了,二者都是内存的分配(堆上),但是make只用于slice、map以及channel的初始化(非零值);而new用于类型的内存分配,并且内存置为零。所以在我们编写程序的时候,就可以根据自己的需要很好的选择了。

make返回的还是这三个引用类型本身;而new返回的是指向类型的指针。

其实new不常用

所以有new这个内置函数,可以给我们分配一块内存让我们使用,但是现实的编码中,它是不常用的。我们通常都是采用短语句声明以及结构体的字面量达到我们的目的,比如:

`i:=0``u:=user{}`

这样更简洁方便,而且不会涉及到指针这种比麻烦的操作。

make函数是无可替代的,我们在使用slice、map以及channel的时候,还是要使用make进行初始化,然后才才可以对他们进行操作

iota #

iota是golang语言的常量计数器 ,只能在常量表达中使用

type pow int
type control byte
const (
	ONE pow = 1 << iota
	TWO
	THREE
	FOUR

	L control = 37 + iota
	U
	R
	D
)
func main() {
    fmt.Println(ONE,TWO,THREE,FOUR)
	fmt.Println(L,R,U,D)
}

#output
1 2 4 8
41 43 42 44