2、Go基础语法:变量声明、常量与iota、基本数据类型与类型转换
好,咱们正式开始啃Go的语法基础。说实话,Go的语法设计得很「干净」,没有那么多花里胡哨的东西。但越是简单的东西,越容易在细节上翻车。我带过不少新人,很多bug其实就出在变量声明和类型转换上。
今天这一章,咱们把变量、常量、基本类型和零值机制一次性讲透。你跟着我走一遍,以后写Go代码会顺手很多。
2.1 变量声明:三种方式,各有各的讲究
Go里声明变量有三种方式,我按使用频率给你排个序。
2.1.1 var 声明 —— 最正统的方式
var 是Go最原始的声明方式。它的好处是——清晰、明确、适合包级别变量。
var name string = "张三"
var age int = 25
var isStudent bool = true
你也可以省略类型,让编译器自己推断:
var name = "张三" // 编译器自动推断为 string
var age = 25 // 推断为 int
我个人习惯:在函数外面(包级别)声明变量时,我一定用 var。为什么?因为包级别变量如果用短变量声明,编译器会报错。这是Go的硬性规定。
2.1.2 短变量声明 := —— 最常用的方式
在函数内部,我几乎只用 :=。它简洁、高效,写起来很爽。
func main() {
name := "张三"
age := 25
fmt.Println(name, age)
}
:= 会同时完成声明和初始化,而且类型由编译器自动推断。你想想看,这比Java那种 String name = "张三"; 省了多少事?
:= 只能在函数内部使用。在函数外面用,编译器会直接报错。我曾经见过一个新手把包级别变量写成 name := "张三",然后一脸懵地问我为什么编译不过。
2.1.3 多变量声明 —— 批量处理的好帮手
Go支持一次声明多个变量,这在处理多个返回值时特别有用。
// 方式一:使用 var
var x, y int = 10, 20
// 方式二:使用短变量
a, b := "hello", 42
// 方式三:不同类型混搭
var (
name string = "张三"
age int = 25
height float64 = 1.75
)
我在项目中遇到过最典型的场景——调用一个返回多个值的函数:
file, err := os.Open("test.txt")
if err != nil {
// 处理错误
}
这种写法在Go里随处可见。说白了,多变量声明就是为这种「函数返回多个值」的场景量身定做的。
2.2 常量与 iota —— 枚举的优雅实现
常量用 const 关键字声明。Go的常量在编译时确定,所以性能上没有任何开销。
const Pi = 3.14159
const MaxRetries = 3
但真正让我觉得Go设计得巧妙的地方,是 iota。它用来生成一组递增的常量,特别适合做枚举。
const (
StatusPending = iota // 0
StatusApproved // 1
StatusRejected // 2
StatusCancelled // 3
)
为什么会从0开始?因为 iota 在 const 块中每出现一次就自增1。你也可以跳过某些值:
const (
_ = iota // 0,用下划线忽略
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30
)
我记得有一次在项目中定义HTTP状态码枚举,用 iota 配合位运算,几行代码就搞定了原本需要几十行switch-case的逻辑。嗯,这就是Go的哲学——用简单的语法解决复杂的问题。
iota 只在 const 块中有效,每遇到一个 const 块就重置为0。
2.3 基本数据类型 —— 就这几种,但够用
Go的基本类型不多,我列个表给你看:
| 类型 | 名称 | 默认零值 | 说明 |
|---|---|---|---|
int |
整型 | 0 | 32位系统占4字节,64位系统占8字节 |
int8 ~ int64 |
指定长度整型 | 0 | 明确指定位数,节省内存 |
uint |
无符号整型 | 0 | 只能存非负数 |
float32 |
单精度浮点 | 0.0 | 精度约6位小数 |
float64 |
双精度浮点 | 0.0 | 精度约15位小数,推荐使用 |
bool |
布尔型 | false | 只有 true 和 false |
string |
字符串 | ""(空字符串) | UTF-8编码,不可变 |
这里我想特别说两点:
第一,关于 float 的选择。 我个人建议,除非你有特殊的内存限制,否则一律用 float64。为什么?因为Go标准库里的数学函数(比如 math.Sqrt)都要求 float64 参数。你用 float32 还得来回转换,麻烦不说,还容易丢精度。
第二,string 是不可变的。 这意味着你不能像C++那样直接修改字符串中的某个字符。我曾经在项目中试图用 s[0] = 'a' 来修改字符串,结果编译直接报错。正确的做法是转成 []byte 再改。
2.4 零值机制 —— Go的贴心设计
Go有一个很人性化的设计——变量声明后如果没有显式赋值,会自动赋予一个「零值」。这跟C语言那种未初始化变量里存着随机值的情况完全不同。
var i int // i = 0
var f float64 // f = 0.0
var b bool // b = false
var s string // s = ""
var p *int // p = nil
你想想看,这能避免多少bug?我见过太多C语言的bug是因为忘记初始化变量,导致程序行为不可预测。Go直接把这个坑给填平了。
2.5 类型转换 —— Go是强类型语言,别想偷懒
Go是强类型语言,这意味着不同类型之间不能隐式转换。你必须显式地写转换代码。
var i int = 42
var f float64 = float64(i) // 必须显式转换
var s string = string(i) // 注意:这是把整数转成对应的Unicode字符,不是数字字符串
这里有个常见的坑——把整数转成字符串。很多人以为 string(42) 会得到 "42",但实际上得到的是 "*"(ASCII码42对应的字符)。正确的做法是用 strconv.Itoa(42)。
我曾经在代码审查时看到过这样的bug:
// 错误写法
port := 8080
addr := "localhost:" + string(port) // 结果:localhost:✈(乱码)
// 正确写法
addr := "localhost:" + strconv.Itoa(port) // 结果:localhost:8080
嗯,这种错误在Go新手身上特别常见。记住一句话:数值转字符串,用 strconv 包,别用 string()。
另外,浮点转整型会直接截断小数部分,不会四舍五入:
var f float64 = 3.99
var i int = int(f) // i = 3,不是4
如果你需要四舍五入,得用 math.Round 先处理一下。
好了,这一章的内容就到这里。变量声明、常量与iota、基本数据类型、零值机制、类型转换——这些都是Go最基础的语法,但也是你写出健壮代码的基石。下一章咱们聊控制结构,if、for、switch这些,到时候你会发现,Go在这些地方也有很多「反常识」的设计。