代码块和作用域

代码块

一个代码块就是放置在一对大括号({})内的一系列声明和语句。除了明显的在一对大括号({})的块之外,还有一些不显眼的块:

  • 所有Go源文本的整体块,这个是最大的一个块,也被称为全域代码块。
  • 任何一个package都是一个所有package源文件包含的包块,也被称为package代码块。比如package model。
  • 每一个源文件都是一个代码块,也被称为源码文件代码块。比如:hello.go
  • 每一个if、for、switch和select语句都认为它们在一个隐含的块之中。
  • 每一个switch、select语句中的的子句都是一个代码块。

作用域

Go的作用域是分块的:

  • 预定义标识符的作用域是全域的代码块;
  • 顶层(在函数外的)的常量、类型、变量、函数(不包括方法)的作用域是package代码块;
  • 导入package包的标识符的作用域,是导入文件的源码文件代码块;
  • 函数参数或是返回值变量的域,是整个函数体;
  • 函数内的常量或是变量标识符开始于声明的位置,结束于所在代码块的结尾;
  • 函数内的类型标识符开始于类型声明处,结束于所在代码块的结尾;一个块的标识符可以在内层块中再声明,这种情况下标识符的作用域是在内层代码块的作用域。

注意:package并不是一个声明,包名不属于任何域。它的意义就是标识某一个源文件所属的包,以及指定被导入的时候默认包名。

变量作用域

Go 语言中变量可以在三个地方声明:

  • 函数内定义的变量称为局部变量
  • 函数外定义的变量称为全局变量
  • 函数定义中的变量称为形式参数

当全局变量和局部变量或形式参数重名的时候,在函数北部局部变量和形式参数会优先。

pro05_1_1.go

  1. //全局变量
  2. var (
  3. length float64 = 3
  4. width float64 = 5
  5. area float64 = 15
  6. )
  7. func Area(length float64, width float64) float64 {
  8. fmt.Println("形式参数(length):", length, "形式参数(width):", width)
  9. return length * width
  10. }
  11. func main() {
  12. fmt.Println("全局变量(length):", length, "全局变量(width):", width)
  13. fmt.Println("全局变量(area):", area)
  14. area := Area(7, 8) //注意此处我传入的length和width与全局变量的值不同
  15. fmt.Println("局部变量(area):", area)
  16. }

输出结果如下:
全局变量(length): 3 全局变量(width): 5
全局变量(area): 15
形式参数(length): 7 形式参数(width): 8
局部变量(area): 56

网上有一位朋友遇到了一个GO变量作用域重名引起的问题,我们来分析分析,源代码如下:
pro05_1_2.go

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. var p *int
  6. func foo() (*int, error) {
  7. var i int = 5
  8. return &i, nil
  9. }
  10. func bar() {
  11. //use p
  12. fmt.Println(*p)
  13. }
  14. func main() {
  15. p, err := foo()
  16. if err != nil {
  17. fmt.Println(err)
  18. return
  19. }
  20. bar()
  21. fmt.Println(*p)
  22. }

编译的时候可以顺利的通过,但是执行的时候会显示:

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,于是导致程序崩溃。程序应该修改如下:

  1. func main() {
  2. var err error
  3. p, err = foo()
  4. if err != nil {
  5. fmt.Println(err)
  6. return
  7. }
  8. bar()
  9. }

链接