Go 变量总结
命名规则
命名规则同C语言。关键字不能用于命名:
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
预定义标识符也不能用于命名:
内建常量:
true false iota nil
内建类型:
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error
内建函数:
make len cap new append copy close delete
complex real imag
panic recover
声明和定义
在C中,声明和定义是完全不同的概念,主要解决编译单元分离的问题。
在Go中,通常声明就是定义。
// var
var age = 18 // 自动推导为int
var age int // 0
var age int = 18
/* 短变量声明
- 同时完成了声明,定义,初始化.
- 只能在函数内部使用
- 不要把:=当做赋值运算符来使用
- 使用:=定义变量时,不能指定var关键字和数据类型
- 变量组中不能够使用:=
- 通过:=同时定义多个变量, 必须给所有变量初始化
*/
a := 10
//-------------------
// 单行定义多个变量
var num1, num2 int
var num1, num2 int = 3, 4
var num1, num2 = 3, 4
num1, num2 := 3, 4
// 变量组
var(
num1 int
num2 float32
)
var(
num1 int = 3
num2 float32 = 3.14
)
var(
num1 = 3
num2 = 4
)
推导规则
- 整数字面量(如
18)的默认类型是:int。 - 浮点数字面量(如
3.14)的默认类型是:float64。 - 复数字面量(如
1i)的默认类型是:complex128。
默认初始化
Go语言的默认初始化和C不一样:一切皆有零值,所有的默认初始化都是零初始化。
- 数字类型 (
int,float, etc.):0 - 布尔类型 (
bool):false - 字符串 (
string):""(空字符串,不是 nil) - 指针、接口、切片、映射、通道:
nil - 结构体 (
struct): 结构体本身不是 nil,而是内部每一个字段都被初始化为对应的零值。
类型转换
Go语言中只能显式转换
var num1 int8 = 20
var num2 int16 = 30
num1 = num2 // 编译报错:不能隐式转换
num2 = int16(num1) // ok
num1 = int8(num2) // ok, narrow convert,可能存在精度丢失
变量类型
基本类型
布尔
| 类型 | 描述 |
|---|---|
bool |
只能是 true 或 false。 |
数值
| 类型 | 描述 | 默认值 |
|---|---|---|
int8, int16, int32, int64 |
有符号整数(表示正负数),数字代表位数。 | 0 |
uint8, uint16, uint32, uint64 |
无符号整数(只表示非负数)。 | 0 |
int, uint |
依赖于操作系统架构(通常是 32 位或 64 位)。 | 0 |
byte |
uint8 的别名,常用于处理 ASCII 字符和原始字节数据。 |
0 |
rune |
int32 的别名,专门用于表示一个 Unicode 码点(即一个字符)。 |
0 |
float32 |
32 位浮点数。 | 0.0 |
float64 |
64 位浮点数(Go 中推荐和常用类型)。 | 0.0 |
complex64 |
32 位实部和 32 位虚部。 | 0 + 0i |
complex128 |
64 位实部和 64 位虚部(推荐和常用类型)。 | 0 + 0i |
字符串
| 类型 | 描述 | 默认值 |
|---|---|---|
string |
字符串是不可变的字节序列,通常存储 UTF-8 编码的文本。 | "" (空字符串) |
聚合类型
- 数组,固定长度。
- 结构体
引用类型
这些类型通常在运行时动态分配内存,并通过引用来管理数据。
- Slice:动态数组
- Map:哈希表
- 指针:同C/C++。
生命周期
变量生命周期取决于其作用域和存储位置:
- 全局/包级变量: 从程序启动开始,直到程序结束, 通常存储在程序的全局数据区(类似于 C 语言的静态/全局区)。
- 局部变量:从函数调用开始,直到不再被引用,由编译器通过逃逸分析决定分配在栈上还是堆上。
内存分配
栈上分配:速度快,无需 GC 参与,内存管理简单。
栈上分配条件:变量的生命周期不会超过定义它的函数的作用域。
堆上分配: 速度慢于栈分配,需要 GC 参与回收。
堆上分配规则:只要变量的引用逃逸出其定义的作用域,它就必须被分配在堆上,以确保在函数返回后,外部引用仍然能访问到它。
举例:
- 当一个局部变量的地址作为指针被函数返回时。
- 赋值给全局变量或包级变量。
- 编译器在编译时无法确定大小的变量时,可能会被分配到堆上。
性能优化
目标:减少GC的工作量,减少不必要的堆分配。
- 避免返回指针。
- 预分配切片、Map的容量。
- 使用sync.Pool缓存对象。
使用工具诊断内存逃逸问题:
go tool compile -m main.go | grep move to heap