Go 学习笔记(二):基础语法与数据类型

写在前面

本文是 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)
  • 函数(多返回值、可变参数、一等公民)
  • 指针基础

下一篇将学习复合数据类型:数组、切片、字典和结构体。