命名规则

命名规则同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 只能是 truefalse

数值

类型 描述 默认值
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