5.1 代码块和作用域

优质
小牛编辑
124浏览
2023-12-01

代码块和作用域

代码块

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

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

作用域

Go的作用域是分块的:

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

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

变量作用域

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

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

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

pro05_1_1.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()
}