代码块和作用域
代码块
一个代码块就是放置在一对大括号({})内的一系列声明和语句。除了明显的在一对大括号({})的块之外,还有一些不显眼的块:
- 所有Go源文本的整体块,这个是最大的一个块,也被称为全域代码块。
- 任何一个package都是一个所有package源文件包含的包块,也被称为package代码块。比如package model。
- 每一个源文件都是一个代码块,也被称为源码文件代码块。比如:hello.go
- 每一个if、for、switch和select语句都认为它们在一个隐含的块之中。
- 每一个switch、select语句中的的子句都是一个代码块。
作用域
Go的作用域是分块的:
- 预定义标识符的作用域是全域的代码块;
- 顶层(在函数外的)的常量、类型、变量、函数(不包括方法)的作用域是package代码块;
- 导入package包的标识符的作用域,是导入文件的源码文件代码块;
- 函数参数或是返回值变量的域,是整个函数体;
- 函数内的常量或是变量标识符开始于声明的位置,结束于所在代码块的结尾;
- 函数内的类型标识符开始于类型声明处,结束于所在代码块的结尾;一个块的标识符可以在内层块中再声明,这种情况下标识符的作用域是在内层代码块的作用域。
注意:package并不是一个声明,包名不属于任何域。它的意义就是标识某一个源文件所属的包,以及指定被导入的时候默认包名。
变量作用域
Go 语言中变量可以在三个地方声明:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
当全局变量和局部变量或形式参数重名的时候,在函数北部局部变量和形式参数会优先。
//全局变量
var (
length float64 = 3
width float64 = 5
area float64 = 15
)
func Area(length float64, width float64) float64 {
fmt.Println("形式参数(length):", length, "形式参数(width):", width)
return length * width
}
func main() {
fmt.Println("全局变量(length):", length, "全局变量(width):", width)
fmt.Println("全局变量(area):", area)
area := Area(7, 8) //注意此处我传入的length和width与全局变量的值不同
fmt.Println("局部变量(area):", area)
}
输出结果如下:
全局变量(length): 3 全局变量(width): 5
全局变量(area): 15
形式参数(length): 7 形式参数(width): 8
局部变量(area): 56
网上有一位朋友遇到了一个GO变量作用域重名引起的问题,我们来分析分析,源代码如下:
pro05_1_2.go
package main
import (
"fmt"
)
var p *int
func foo() (*int, error) {
var i int = 5
return &i, nil
}
func bar() {
//use p
fmt.Println(*p)
}
func main() {
p, err := foo()
if err != nil {
fmt.Println(err)
return
}
bar()
fmt.Println(*p)
}
编译的时候可以顺利的通过,但是执行的时候会显示:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x1 addr=0x0 pc=0x401121]
goroutine 1 [running]:
panic(0x4f04c0, 0xc082006080)
D:/Go/src/runtime/panic.go:481 +0x3f4
这个问题是怎么引起的哪?就是因为全局变量和局部变量重名了:
p, err := foo()
在GO中 := 表示定义加赋值,在上句中等于又定义了一个p,这导致在函数bar调用全局变量P的时候,全局变量P并没有被赋值,依然指向nil,于是导致程序崩溃。程序应该修改如下:
func main() {
var err error
p, err = foo()
if err != nil {
fmt.Println(err)
return
}
bar()
}