写在前面
本文是 Go 学习笔记系列的第二篇,介绍 Go 的基础语法,包括变量声明、数据类型、常量、流程控制和函数。前置知识:已完成 Go 环境搭建(第一篇)。
一、变量声明
1.1 var 声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 基本声明(类型在变量名后面)
var name string = "Go"
// 类型推断(省略类型)
var age = 18
// 声明但不赋值(使用零值)
var score int // 0
var active bool // false
var msg string // ""(空字符串)
// 批量声明
var (
x int
y int
flag bool
)
|
1.2 短变量声明 :=
1
2
3
4
5
6
7
8
9
|
// 只能在函数内使用
name := "Go" // 等价于 var name string = "Go"
age := 18 // 等价于 var age int = 18
x, y := 1, 2 // 多变量同时声明
// 注意:左边至少要有一个新变量才能用 :=
name := "Go"
name := "Python" // 编译错误,name 已声明
name, lang := "Go", 1 // 正确,lang 是新变量
|
何时用 var,何时用 :=:函数内优先用 :=;包级别变量、需要零值或指定类型时用 var。
1.3 零值
Go 的变量声明后如果没有赋值,会自动初始化为零值:
1
2
3
4
5
6
7
8
9
10
|
int → 0
float64 → 0.0
bool → false
string → ""
pointer → nil
slice → nil
map → nil
chan → nil
interface → nil
func → nil
|
1.4 匿名变量
1
2
3
4
5
|
// 用 _ 忽略不需要的返回值
_, err := os.Stat("/tmp/test")
if err != nil {
fmt.Println("文件不存在")
}
|
二、基本数据类型
2.1 整型
1
2
3
4
5
6
7
8
9
10
11
|
var a int = 10 // 平台相关(32位或64位)
var b int8 = 127 // -128 到 127
var c int16 = 1000
var d int32 = 100000
var e int64 = 1000000
var f uint = 10 // 无符号,平台相关
var g uint8 = 255 // 0 到 255(也叫做 byte)
var h uintptr // 存放指针
// 实际开发中最常用的是 int 和 int64
// uint8 的别名是 byte,int32 的别名是 rune(表示一个 Unicode 字符)
|
2.2 浮点型
1
2
3
4
5
6
7
|
var pi float32 = 3.14
var e float64 = 2.71828
// 默认推断为 float64
x := 3.14 // float64
// 实际开发中基本都用 float64
|
2.3 布尔型
1
2
3
4
5
|
var active bool = true
var deleted bool // false
// 不能用 0/1 代替布尔值
// var flag bool = 1 // 编译错误
|
2.4 字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
s1 := "Hello" // 双引号,可转义
s2 := `Hello\nWorld` // 反引号,原样输出(\n 不会转义)
s3 := "你好,Go" // UTF-8 编码,天然支持中文
// 字符串拼接
greeting := "Hello" + " " + "Go"
// 字符串长度
fmt.Println(len("Go")) // 2(字节数)
fmt.Println(len("你好")) // 6(每个中文3个字节)
// 字符串不可变(不能修改某个字符)
// s1[0] = 'h' // 编译错误
// 遍历字符串
for i, ch := range "你好Go" {
fmt.Printf("%d: %c\n", i, ch) // 按 rune 遍历,不会乱码
}
|
2.5 字符类型
1
2
3
4
5
6
7
8
|
// byte 是 uint8 的别名,表示 ASCII 字符
// rune 是 int32 的别名,表示一个 Unicode 字符
var ch byte = 'A' // 65
var zh rune = '中' // 20013
fmt.Printf("%c\n", ch) // A
fmt.Printf("%c\n", zh) // 中
|
2.6 类型转换
Go 没有隐式类型转换,必须显式转换:
1
2
3
4
5
6
7
8
9
10
|
var i int = 42
var f float64 = float64(i) // int → float64
var j int = int(f) // float64 → int
// string 和数字互转(需要用 strconv 包)
import "strconv"
s := strconv.Itoa(42) // int → string:"42"
n, err := strconv.Atoi("42") // string → int:42
f := strconv.FormatFloat(3.14, 'f', 2, 64) // float64 → string:"3.14"
|
三、常量
3.1 基本用法
1
2
3
4
5
6
7
8
|
const pi = 3.14159
const greeting string = "Hello"
// 批量声明
const (
StatusOK = 200
StatusError = 500
)
|
3.2 iota 枚举
iota 是 Go 的常量生成器,在 const 块中每行自动加 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
// 跳过值
const (
_ = iota // 0(跳过)
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20
GB // 1 << 30
TB // 1 << 40
)
|
四、流程控制
4.1 if-else
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
age := 18
// 基本用法
if age >= 18 {
fmt.Println("成年")
} else {
fmt.Println("未成年")
}
// if 带初始化语句(常用模式)
if err := doSomething(); err != nil {
fmt.Println("出错:", err)
}
// err 只在 if-else 块内有效
// 多条件
if score >= 90 {
fmt.Println("优秀")
} else if score >= 60 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}
|
Go 的 if 不需要小括号,但花括号必须有。
4.2 for 循环
Go 只有 for 一种循环,没有 while:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// 经典 for
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// 当作 while 用
n := 0
for n < 10 {
fmt.Println(n)
n++
}
// 无限循环
for {
// break 或 return 退出
break
}
// for-range 遍历(类似 foreach)
names := []string{"Go", "Python", "Java"}
for index, value := range names {
fmt.Println(index, value)
}
// 只要 value 不要 index
for _, value := range names {
fmt.Println(value)
}
|
4.3 switch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
day := "Monday"
// 基本用法(自动 break,不需要手动加)
switch day {
case "Monday":
fmt.Println("周一")
case "Tuesday":
fmt.Println("周二")
default:
fmt.Println("其他")
}
// 多值匹配
switch score {
case 90, 100:
fmt.Println("优秀")
case 80:
fmt.Println("良好")
}
// 无条件 switch(替代 if-else if 链)
score := 85
switch {
case score >= 90:
fmt.Println("优秀")
case score >= 80:
fmt.Println("良好")
case score >= 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
// fallthrough:穿透到下一个 case(少用)
switch x {
case 1:
fmt.Println("1")
fallthrough // 无条件执行下一个 case
case 2:
fmt.Println("2")
}
|
4.4 defer
defer 延迟执行,在函数返回前执行,常用于资源释放:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
func readFile(path string) {
file, err := os.Open(path)
if err != nil {
return
}
defer file.Close() // 函数返回前自动关闭文件
// 读取文件...
}
// 多个 defer 按 LIFO(后进先出)顺序执行
func main() {
defer fmt.Println("第一")
defer fmt.Println("第二")
defer fmt.Println("第三")
fmt.Println("先执行")
}
// 输出:
// 先执行
// 第三
// 第二
// 第一
// defer 的参数在声明时就确定了
func main() {
x := 1
defer fmt.Println(x) // 输出 1,不是 2
x = 2
}
|
4.5 break 和 continue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// break 跳出当前循环
for i := 0; i < 10; i++ {
if i == 5 {
break
}
fmt.Println(i) // 0 1 2 3 4
}
// continue 跳过本次迭代
for i := 0; i < 10; i++ {
if i%2 == 0 {
continue
}
fmt.Println(i) // 1 3 5 7 9
}
// 跳出外层循环(使用标签)
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break outer // 跳出外层循环
}
}
}
|
五、函数
5.1 基本定义
1
2
3
4
5
6
7
8
9
10
11
12
|
// 基本函数
func add(a int, b int) int {
return a + b
}
// 相同类型的参数可以省略前面的类型
func add(a, b int) int {
return a + b
}
// 调用
result := add(1, 2)
|
5.2 多返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// Go 函数可以返回多个值(非常常用)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
// 调用
result, err := divide(10, 3)
if err != nil {
fmt.Println("出错:", err)
} else {
fmt.Println("结果:", result)
}
|
5.3 命名返回值
1
2
3
4
5
6
|
// 给返回值命名,可以在函数内直接赋值
func rectangle(length, width float64) (area, perimeter float64) {
area = length * width
perimeter = 2 * (length + width)
return // 裸 return,自动返回 area 和 perimeter
}
|
5.4 可变参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 可变参数(类似 Java 的 ...params)
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// 调用
sum(1, 2, 3) // 6
sum(1, 2, 3, 4, 5) // 15
// 展开切片
numbers := []int{1, 2, 3}
sum(numbers...) // 传切片时加 ...
|
5.5 函数是一等公民
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
// 函数作为变量
add := func(a, b int) int {
return a + b
}
fmt.Println(add(1, 2)) // 3
// 函数作为参数(回调)
func apply(nums []int, fn func(int) int) []int {
result := make([]int, len(nums))
for i, n := range nums {
result[i] = fn(n)
}
return result
}
doubled := apply([]int{1, 2, 3}, func(n int) int {
return n * 2
})
// doubled = [2, 4, 6]
// 函数作为返回值(闭包)
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
|
六、指针
6.1 基本用法
1
2
3
4
5
6
7
8
|
x := 42
p := &x // & 取地址,p 是 *int 类型
fmt.Println(p) // 地址,如 0xc0000b2008
fmt.Println(*p) // * 解引用,输出 42
*p = 100 // 通过指针修改值
fmt.Println(x) // 100
|
6.2 指针作为函数参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 不用指针:值传递,函数内修改不影响外部
func double(n int) {
n = n * 2 // 只修改了副本
}
// 用指针:可以在函数内修改外部变量
func doublePtr(n *int) {
*n = *n * 2
}
x := 10
double(x)
fmt.Println(x) // 10(没变)
doublePtr(&x)
fmt.Println(x) // 20(变了)
|
Go 指针不能做指针运算(不能 p++),比 C 安全得多。实际开发中指针主要用于结构体方法(接收者)和避免大对象拷贝。
七、小结
本文学习了 Go 的基础语法:
- 变量声明(var、:=、零值)
- 基本数据类型(整型、浮点、字符串、布尔)和类型转换
- 常量与 iota 枚举
- 流程控制(if、for、switch、defer)
- 函数(多返回值、可变参数、一等公民)
- 指针基础
下一篇将学习复合数据类型:数组、切片、字典和结构体。