变量和常量
变量
声明、赋值变量
var name type
// 一次声明多个同一类型的变量
var a, b int
// 批量声明变量
var (
a int
b string
c []float32
d func() bool
e struct {
x int
}
)
// 声明变量并赋值
var a int = 10
// 声明变量但是没有初始化的,将会赋予指定数据类型
var b int // b = 0
// 变量类型自动推断
var age = 100 // 自动推断为int
fmt.Println("type=", reflect.TypeOf(age))
// := 简短声明
age := 10 // 自动推断类型为int
// 多变量赋值,a b 变量交换
var a int = 100
var b int = 200
b, a = a, b
fmt.Println(a, b)
匿名变量_
匿名变量的特点是一个下画线“”,“”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。例如:
func GetData() (int, int) {
return 100, 200
}
func main(){
a, _ := GetData()
_, b := GetData()
fmt.Println(a, b)
}
堆和栈空间
栈:线性表,后进先出,适合可预知大小的变量分配

堆:类似于往一个房间里摆放各种家具,家具的尺寸有大有小,分配内存时,需要找一块足够装下家具的空间再摆放家具。堆适合不可预知大小的内存分配,但是速度相对较慢,并且容易出现内存碎片

func calc(a, b int) int {
var c int // 在栈中分配一个指定大小的内存
c = a * b
var x int // 在栈中分配一个指定大小的内存
x = c * 10
return x
}
变量存储在堆还是栈?
Go语言在编译期会帮助开发者判断变量应该放到堆上还是栈上(C++需要开发者自行判断),他的判断规则为:
在编译期间堆变量进行逃逸分析
没有发生:栈
发生了:堆
变量逃逸分析
package main
import "fmt"
// 本函数测试入口参数和返回值情况
func dummy(b int) int {
// 声明一个变量c并赋值
var c int
c = b
return c
}
// 空函数, 什么也不做
func void() {
}
func main() {
// 声明a变量并打印
var a int
// 调用void()函数
void()
// 打印a变量的值和dummy()函数返回
fmt.Println(a, dummy(0))
}
使用如下命令对上面代码进行内存分析:
# -m 内存分配分析
# -l 避免程序内联,也就是避免程序进行优化
go run -gcflags "-m -l" main.go
# command-line-arguments
./main.go:19:13: ... argument does not escape
./main.go:19:13: a escapes to heap # 变量a逃逸到了堆
./main.go:19:22: dummy(0) escapes to heap # dumy(0)的返回值逃逸到了堆
变量逃逸分析:取地址符
package main
import "fmt"
// 声明空结构体测试结构体逃逸情况
type Data struct {
}
func dummy() *Data {
// 实例化c为Data类型
var c Data
//返回函数局部变量地址
return &c
}
func main() {
fmt.Println(dummy())
}
执行go run -gcflags "-m -l" main.go
的结果:
# command-line-arguments
./main.go:8:6: moved to heap: c # 变量c在第八行的位置,因为使用&取地址符,为了保证程序的最终运行结果,所以将变量c从栈转移到了堆空间中
./main.go:13:13: ... argument does not escape
&{}
变量的作用域和生命周期
变量类型
定义位置
生命周期
全局变量
函数外部定义
等同于整个程序的运行周期
局部变量
函数内定义
创建变量的语句开始,到这个变量不再被使用为止
形参
函数签名定义
函数被调用的时候创建,函数调用结束后被销毁
变量逃逸会导致变量作用域的改变
常量
用于存储不会发生改变的数据
在编译时被创建,即使是函数内部的常量
常量值只能是布尔型、数字型、字符串型
定义常量的表达式必须是编译器可以进行求值的常量表达式
显示类型定义
const pi float64 = 3.14159
隐式类型定义:将会由值推断类型
const pi = 3.14159
值必须是编译期间就可以确定的
const c1 = 2/3
const c2 = getNumber() // 引发构建错误: getNumber() 用做值
批量声明常量
const (
e = 2.7182818
pi = 3.1415926
)
模拟枚举:iota常量生成器
package main
import "fmt"
// 声明芯片类型(类型别名)
type ChipType int
const (
None ChipType = iota
CPU // 中央处理器
GPU // 图形处理器
)
// 定义 ChipType 类型的方法 String(),返回值为字符串类型
// String() 是类型进行字符串输出时调用的方法
func (c ChipType) String() string {
switch c {
case None:
return "None"
case CPU:
return "CPU"
case GPU:
return "GPU"
}
return "N/A"
}
func main() {
// 输出CPU的值并以整型格式显示
fmt.Printf("%s %d", CPU, CPU)
}
// 在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加一。
最后更新于
这有帮助吗?