全局变量(类似JS)

在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用。在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑

这段代码原意是定义一个包内全局变量p,用foo()的返回值对p进行初始化,在bar中使用p。预期结果:bar()和main()中均输出5。但编译执行后的结果却是:Crash

var p *int

func foo() (*int, error) {
	var i int = 5
	return &i, nil
}

func bar() {
    fmt.Printf("%p, %T\n", p, p) //output: 0x14dc80, 0×0, *int
	fmt.Println(*p)
}

func main() {
	fmt.Printf("%p, %T\n", p, p) //output: 0x14dc80, 0×0, *int
	p, err := foo()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("%p, %T\n", p, p) //output: 0x2081c6020, 0x20818a258, *int
	bar()
	fmt.Println(*p)
}

问题出在操作符:=,对于使用:=定义的变量,如果新变量与同名已定义的变量不在同一个作用域中,那么 Go 会新定义这个变量。对于本例来说,main() 函数里的 p 是新定义的变量,会遮住全局变量 p,导致执行到bar()时程序,全局变量 p 依然还是 nil,程序随即 Crash

正确的做法是将 main() 函数修改为:

func main() {
	var err error
	p, err = foo()
	if err != nil {
		fmt.Println(err)
		return
	}
	bar()
	fmt.Println(*p)
}

函数的作用域

var f = func(i int) {
	print("x")
}

func main() {
	f := func(i int) {
		print(i)
		if i > 0 {
			f(i - 1)
		}
	}
	f(10)
}

参考答案及解析:10x。这道题一眼看上去会输出 109876543210,其实这是错误的答案,这里不是递归。假设 main() 函数里为 f2(),外面的为 f1(),当声明 f2() 时,调用的是已经完成声明的 f1()。f2还未完成声明。效果类似于下面的代码

var x = 23

func main() {
    x := 2*x - 4
    println(x)    // 输出:42
}

for 语句的变量 a 是重新声明,它的作用范围只在 for 语句范围内

func main() {
	a := 1
	for i := 0;i<5;i++ {
		a := a + 1
		a = a * 2
	}
	fmt.Println(a)
}

 

方法内部的代码块

在golang中方法左括号{是不能换行的,不然编译不通过

func main()
{

}

代码块内部具有独立性质 (代码块内部定义的变量外部无法调用,但是代码块外定义的变量,代码块内可以调用)。代码块是要换行的

func main() {
	x := 1
	fmt.Println(x)

	{ //代码块开头
		fmt.Println(x)
		i, x := 2, 2
		fmt.Println(i, x)
	} //代码块结尾

	fmt.Println(x) // print ?
}