Go - Variable
内存边界
- 在编程语言中,为了方便操作内存特定位置的数据,使用
变量
与特定位置的内存
绑定 编译器
或者解析器
需要知道变量所能引用的内存区域边界
- 动态语言
解析器
可以在运行时
通过对变量赋值的分析
,自动确定变量的边界- 一个变量可以在运行时被赋予大小不同的边界
- 静态语言
编译器
必须明确知道
一个变量的边界才允许使用该变量- 但编译器无法自动分析,因此
边界信息
必须由开发者
提供 -变量声明
- 在具体实现层面,
边界信息
由变量的类型属性
赋予
- 动态语言
变量声明
Go 是静态语言,所有变量在使用前必须先进行
声明
声明:告诉编译器
该变量可以操作的内存的边界信息
(由变量类型信息
提供)
通用
变量声明形式与主流静态语言的差异 - 将
变量名
放在了类型
前面(方便语法糖移除type
)
如果没有
显式
为变量赋予初值,Go编译器
会为变量赋予类型零值
1 | var a int // a 的初值为 int 类型的零值 0 |
Go 的每种
原生类型
都有其默认值
,即类型零值
复合类型(array
、struct
)变量的类型零值为组成元素
都为零值
的结果
原生类型 | 类型零值 |
---|---|
整型 | 0 |
浮点 | 0.0 |
布尔 | FALSE |
字符串 | "" |
pointer、interface、slice、channel、map、func |
nil |
变量声明块
1 | var ( |
多变量声明
1 | var a, b, c int = 1, 2, 3 |
变量声明块 + 多变量声明
1 | var ( |
语法糖
省略类型信息
省去 type -
var varName = initExpression
1 | var a = 13 |
编译器会根据右侧变量初值
自动推导
变量的类型,并给变量赋予初值对应的默认类型
Value | Type |
---|---|
整型值 | int |
浮点值 | float64 |
复数值 | complex128 |
布尔值 | bool |
字符值 | rune |
字符串值 | string |
不使用默认类型,需要
显式
地为变量指定类型(通用变量声明
or显式类型转换
)
1 | var a int32 = 1 |
省略类型信息的语法糖仅适用于:
变量声明
的同时显式赋予变量初值
1 | package main |
1 | $ go build |
结合多变量声明,可以
同时声明多个不同类型的变量
a 类型为int
,b 类型为rune
,c 类型为string
1 | var a, b, c = 1, 'A', "abc" |
短变量声明
省去 var 和 type -
varName := initExpression
短变量声明中的变量类型也是由 Go 编译器
自动推导
出来的
1 | func main() { |
短变量声明也支持多变量声明
1 | func main() { |
变量分类
包级变量
在
包级别
可见的变量,如果包级变量
是Exported
,则为全局变量
包级变量
只能
使用var
关键的变量声明形式,不能使用短变量
声明形式
初始化
饿汉
省略类型信息 -
var varName = initExpression
Go 编译器会自动根据 initExpression 结果值的类型,来确定 varName 的变量类型
1 | var ErrShortWrite = errors.New("short write") |
不使用默认类型,需要显式地为包级变量指定类型
1 | var a = 13 |
饱汉
虽然没有显式初始化,但同样有类型零值
1 | var a int32 |
声明聚类
将同一类的变量声明放在同一个
var 变量声明块
中,提高代码可读性
1 | package net |
就近原则
是否将包级变量的声明
全部
放在源文件的头部
?
- 就近原则:尽可能在靠近
第 1 次使用
变量的位置声明该变量 变量作用域最小化
的一种实现手段
1 | var ErrNoCookie = errors.New("http: named cookie not present") |
一个包级变量在包内被
多次使用
,还是放在源文件头部
声明比较合适
局部变量
在 Go
函数
或者方法
体内声明的变量,仅在函数或方法体内可见
支持
短变量
声明形式,局部变量特有
,也是使用最多
的一种声明形式
初始化
饱汉
延迟初始化,采用
通用
的变量声明形式
省略类型信息声明
和短变量声明
,均不支持变量的延迟初始化
- 因为 Go
编译器
依赖变量初值
,进行自动推导
- 因为 Go
- 因此,与
包级变量
一样,如果是延迟初始化都只能采用通用的变量声明形式
1 | func main() { |
饿汉
显式初始化,采用
短变量
声明形式
短变量
声明形式是局部变量最常用
的声明形式
1 | func main() { |
不使用默认类型,可以通过
类型转换
,保持变量声明的一致性
1 | func main() { |
分支控制
尽量在分支控制时使用
短变量
的声明形式
- 分支控制是 Go 中
短变量
声明形式应用最广泛的场景 - Go 程序,
很少单独声明
用于分支控制语句中的变量- 而是通过
短变量
声明形式,将它们与if
、for
等控制语句融合在一起 - 在控制语句中
直接声明
用于控制语句代码块中的变量
- 而是通过
短变量声明 + 分支控制
,也很好地体现了就近原则
,让变量的作用域最小化
1 | func LastIndexAny(s, chars string) int { |
声明聚类
设计良好
的函数或方法,都追求单一职责
,一般规模都不大- 因此,很少需要应用
var 变量声明块
来聚类局部变量
1 | func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (addrList, error) { |