作用域
作者: ryan 发布于: 2025/7/11 更新于: 2025/7/11 字数: 0 字 阅读: 0 分钟
作用域是指一个标识符(变量、常量、函数等)的可见范围,即标识符在代码中可以被访问的区域。
作用域的类型
- 全局作用域:标识符在整个程序中可见,通常定义在包的顶层代码中。
- 局部作用域:标识符在某个封闭空间内可见,例如函数内部。
作用域的分类
宇宙块
宇宙块(Universe Block):宇宙块,意思就是全局块,不过是语言内建的。预定义的标识符就在这个全局环境中,因此bool、int、
nil、true、false、iota、append等标识符全局可见,随处可用。
语句块作用域
if、for、switch等语句中使用短格式定义的变量,可以认为就是该语句块的变量,作用域仅在该语句块中。
go
package main
import "fmt"
func test() {
a := 100 //局部变量,本地变量
var b = 200 //局部变量
fmt.Println(a, b)
}
func main() {
fmt.Println(a, b) // 编译错误!无法访问 test 的局部变量
test()
}
for
循环的作用域
go
s := []int{1, 3, 5}
for i, v := range s { // i, v 的作用域仅限 for 块内
fmt.Println(i, v) // ✅ 内部可见
}
fmt.Println(i, v) // ❌ 编译错误:i, v 超出作用域[1][6]
迭代变量 i
、v
在循环结束时销毁,外部访问会导致编译错误。
if
语句的作用域
go
if f, err := os.Open("o:/t.txt"); err != nil {
fmt.Println(f, err) // ✅ 内部可见
}
fmt.Println(f, err) // ❌ 编译错误:f, err 超出作用域[2][10]
初始化变量(如 f
、err
)的作用域覆盖整个 if-else
块(包括所有分支)外部访问这些变量会触发编译错误。
switch
/select
语句的作用域
go
x := 1
switch y := x + 1; y { // y 的作用域覆盖所有 case
case 2:
fmt.Println(y) // ✅ 子句中可见
default:
fmt.Println(y) // ✅ 子句中可见
}
fmt.Println(y) // ❌ 编译错误:y 超出作用域[6][7]
switch
的初始化变量(如 y
)在 case
子句内有效,但外部不可访问。
显式的块作用域
在任何一个大括号中定义的标识符,其作用域只能在这对大括号中。
go
{
const a = 100 // 常量仅在此块内有效
var b = "hello" // 变量仅在此块内有效
c := true // 短变量声明仅在此块内有效
fmt.Println(a, b, c) // ✅ 块内可见
}
fmt.Println(a, b, c) // ❌ 编译错误:标识符未定义[1][4][6]
包块
(Package Block)包内全局可见的标识符,定义在包的顶层代码中。
每一个package包含该包所有源文件,形成的作用域。有时在包中顶层代码定义标识符,也称为全局标识符。 所有包内定义全局标识符,包内可见。包的顶层代码中标识符首字母大写则导出,从而包外可见,使用时也要加上包名。例如 fmt.Prinf()
。
- 小写首字母:包内可见,包外不可见。
- 大写首字母:包外可导出,需通过包名访问(如
fmt.Println
)。
函数作用域
go
package main
import "fmt"
// 包级作用域(整个包可见)
const a = 100 // 常量不可取地址
var b = 200
var d = 400
func showB() int {
return b // 访问包级变量b
}
func main() {
// 1. 常量与变量遮蔽 - 包级 vs 局部
fmt.Println(1, a) // 输出包级常量a=100
var a = 500 // 局部变量遮蔽包级常量a
fmt.Println(2, a, &a) // 输出局部变量a=500及其地址
// 2. 全局变量修改与局部遮蔽
fmt.Println(3, b, &b) // 包级变量b=200
b = 600 // 修改包级变量值
fmt.Println(3.1, b, &b) // b=600(地址不变)
b := 601 // 局部变量遮蔽包级b [6][9]
fmt.Println(3.2, b, &b) // 输出局部b=601(地址变了->新地址)
fmt.Println(3.3, showB()) // 显示包级b=600 之前修改了包级变量值200->600(不受局部遮蔽影响)
// 3. 嵌套代码块作用域
{
const j = 'A' // 块级常量(仅当前块可见)
b := 800 // 遮蔽外层的局部b(main中的b=601)
fmt.Println(4, a, b) // a=500(main局部), b=800
{
x := 900 // 内层块变量(仅当前块可见)
fmt.Println(4.1, x) // 输出900
}
// fmt.Println(x) // 编译错误:x超出作用域
}
// fmt.Println(j) // 编译错误:j超出作用域
fmt.Println(4.4, b) // 恢复main的局部b=601
// 4. 控制语句作用域
for i, v := range []int{1, 3, 5} {
fmt.Println(i, v) // i,v仅在循环内可见 [6][10]
}
// fmt.Println(i, v) // 编译错误
}
- 内层作用域同名标识符遮蔽外层(如局部
a=500
遮蔽包级常量a=100
) - 局部
b:=601
和块级b:=800
均遮蔽包级变量b
,但不修改其原始值。
变量查找顺序:内层 → 外层(从最近作用域向外穿透)
作用域边界
{}
代码块:内部变量(如x=900
)外部不可访问for
循环:i,v
仅在循环内部可见- 常量保护:包级常量
a
不可取地址(&a
非法)
作用域的规则
- 向内穿透:内层作用域可访问外层变量(如函数内访问包级变量)。
- 就近优先:内层定义同名标识符时,优先使用最近的定义(遮蔽外层)。
- 对外不可见:外层无法访问内层作用域中的标识符(如函数外无法访问函数内变量)。