前言
环境搭好(见上篇),开始正式修炼 Go 语法。Go 的设计哲学是 少即是多——关键字只有 25 个,没有继承、没有异常、没有三元运算符,但每个特性都精打细磨。
一、变量与常量
package main
import "fmt"
func main() {
// 三种声明方式
var a int = 10 // 完整声明
var b = 3.14 // 类型推断
c := "hello" // 短声明(只能用在函数内, 最常用)
// 多变量
x, y := 1, 2
x, y = y, x // 交换变量, 不需要中间变量!
// 常量(编译期确定, 不可修改)
const Pi = 3.14159
const (
StatusOK = 200
StatusErr = 500
)
// iota: 自动递增的常量生成器
const (
Mon = iota + 1 // 1
Tue // 2
Wed // 3
)
fmt.Println(a, b, c, x, y, Pi, Mon, Tue, Wed)
}
⚠️ Go 没有"未使用警告"那么温柔——声明了不用的变量直接编译报错,逼你保持代码干净。
二、基本类型与零值
| 类别 | 类型 | 零值 |
|---|---|---|
| 整型 | int, int8/16/32/64, uint... |
0 |
| 浮点 | float32, float64 |
0.0 |
| 布尔 | bool |
false |
| 字符串 | string |
""(空串, 不是 nil) |
| 指针/切片/map/接口/函数/channel | *T, []T, map[K]V… |
nil |
Go 没有隐式类型转换,必须显式:
var i int = 42
var f float64 = float64(i) // 必须显式转
var u uint = uint(f)
字符串常用操作:
s := "hello, 世界"
len(s) // 字节数: 13(UTF-8 下汉字 3 字节)
utf8.RuneCountInString(s) // 字符数: 9
strings.Contains(s, "世界") // true
strings.Split("a,b,c", ",") // [a b c]
fmt.Sprintf("%s=%d", "n", 5) // 格式化字符串
三、指针(比 C 简单)
func main() {
x := 10
p := &x // p 是指向 x 的指针
fmt.Println(*p) // 解引用: 10
*p = 20
fmt.Println(x) // 20
}
Go 指针不能做算术运算(不能 p++),避免了 C 的一整类内存错误。
四、函数:多返回值是杀手锏
// 多返回值 + 命名返回值
func divide(a, b int) (result int, err error) {
if b == 0 {
err = fmt.Errorf("除数不能为零")
return // 裸返回
}
result = a / b
return
}
// 可变参数
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// 函数是一等公民: 可以赋值、传参
f := func(x int) int { return x * x }
fmt.Println(f(5))
调用侧的惯用法(后面错误处理那篇会展开):
r, err := divide(10, 0)
if err != nil {
fmt.Println("出错了:", err)
}
五、控制流:只有 for
Go 把所有循环统一成了 for:
// 经典三段式
for i := 0; i < 5; i++ { ... }
// 相当于 while
n := 0
for n < 10 { n++ }
// 死循环
for { break }
// 遍历切片
nums := []int{1, 2, 3}
for i, v := range nums { fmt.Println(i, v) }
// 遍历 map
m := map[string]int{"a": 1}
for k, v := range m { fmt.Println(k, v) }
// 不需要下标时用 _ 丢弃
for _, v := range nums { ... }
if 的特殊语法——可以带初始化语句(限定了变量作用域):
if err := doSomething(); err != nil {
fmt.Println("failed:", err)
}
// 这里已经访问不到 err 了
switch 更强大——默认 break,一个 case 可多值:
switch day := 3; day {
case 1, 2, 3, 4, 5:
fmt.Println("工作日")
case 6, 7:
fmt.Println("周末")
default:
fmt.Println("非法")
}
// 无表达式 switch, 替代长串 if-else
switch {
case score >= 90:
grade = "A"
case score >= 60:
grade = "B"
default:
grade = "C"
}
六、defer:延迟执行的清理利器
func readFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // 函数返回前必定执行, 不怕中间 return
// ... 读文件逻辑
return nil
}
要点:
- 多个 defer 后进先出(栈):
defer a(); defer b()→ 执行顺序 b → a - defer 的参数在声明时求值:
i := 0
defer fmt.Println(i) // 打印 0, 不是 1
i = 1
- 典型用途:关文件、解锁
mu.Unlock()、recover 捕获 panic
小结
| 特性 | 一句话 |
|---|---|
:= |
函数内短声明,最常用 |
| 零值 | 没初始化也不会是"随机垃圾值" |
| 多返回值 | (result, err) 惯例的根基 |
| for | 唯一的循环关键字 |
| switch | 默认 break,case 可多值 |
| defer | 资源清理标配,LIFO |
语法过关后,下一站是复合类型:数组、切片与映射——Go 数据结构的主战场。
本文是「Golang」系列第 2 篇。