适合刚接触 Go 语言的同学,涵盖基础数据类型、流程控制、数组切片、字典、结构体、方法等,并附有 4 个必做练习。
1. 前言
最近我开始学习 Go 语言,经过一天的系统梳理和实战练习,把最基础的知识点过了一遍。Go 是一门静态强类型、编译型、并发支持良好的语言,语法简洁,特别适合后端开发、云原生和中间件编写。
这篇文章将按照我学习的主线,总结 Go 的核心语法,并提供 4 个练习题(成绩统计、字符计数、数字金字塔、图书管理),最后附上完整代码思路。如果你也在入门阶段,希望这篇文章对你有帮助。
2. 基础数据类型
Go 的基本类型包括:
go
bool // true / false
string // 字符串,UTF-8
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64
byte // uint8 别名
rune // int32 别名,表示 Unicode 码点
float32 float64
complex64 complex128
重点:
rune处理中文等 Unicode 字符,byte处理 ASCII。变量声明可以用
var name type或短变量:=。
go
var age int = 18
name := "Alice"
var isOk bool
3. 输入与输出
-
输出 :
fmt.Print/fmt.Printf/fmt.Println -
输入 :
fmt.Scan/fmt.Scanf/fmt.Scanln
go
var x int
fmt.Print("请输入数字:")
fmt.Scan(&x)
fmt.Printf("你输入的是:%d\n", x)
4. 运算符
| 分类 | 运算符 |
|---|---|
| 算术 | + - * / % ++ -- |
| 比较 | == != < > <= >= |
| 逻辑 | `&& |
| 位运算 | `& |
| 赋值 | = += -= *= /= %= ... |
注意 :++ 和 -- 只能独立使用,不能作为表达式(c = a++ 是错误的)。
5. 流程控制
5.1 if
go
if score >= 90 {
fmt.Println("优秀")
} else if score >= 60 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}
支持在条件前加简单语句,作用域仅限于 if 块。
5.2 switch
go
switch day {
case 1, 3, 5:
fmt.Println("奇数日")
case 2, 4, 6:
fmt.Println("偶数日")
default:
fmt.Println("周日")
}
不需要 break,默认自动终止。如需穿透,使用 fallthrough。
5.3 for 循环
Go 只有 for,没有 while。
go
// 经典 for
for i := 0; i < 10; i++ {}
// while 形式
sum := 1
for sum < 100 {
sum += sum
}
// 无限循环
for {
break
}
5.4 range 遍历
go
nums := []int{2,4,6}
for i, v := range nums {
fmt.Println(i, v)
}
m := map[string]int{"a":1, "b":2}
for k, v := range m {
fmt.Println(k, v)
}
for _, ch := range "Go语言" {
fmt.Printf("%c ", ch) // G o 语 言
}
6. 指针
-
*T表示指向T的指针,零值为nil。 -
&取地址,*解引用。 -
没有指针运算 (不能
p++)。
go
func setZero(x *int) {
*x = 0
}
a := 10
setZero(&a)
fmt.Println(a) // 0
7. 数组和切片
7.1 数组(固定长度,值类型)
go
var arr [3]int = [3]int{1,2,3}
arr2 := [...]int{4,5,6} // 由编译器确定长度
7.2 切片(动态数组,引用类型)
go
// 创建
s1 := []int{1,2,3}
s2 := make([]int, 5, 10) // 长度5,容量10
// 追加
s1 = append(s1, 4, 5)
// 切片操作
sub := s1[1:3] // [2,3]
// 复制
copy(dest, src)
8. 字典(map)
必须初始化才能使用,零值为 nil,直接赋值会 panic。
go
// 正确初始化
m := make(map[string]int)
m["age"] = 18
// 字面量
m2 := map[string]string{
"name": "Tom",
}
// 读取、判断存在
v, ok := m["age"]
if ok {
fmt.Println(v)
}
// 删除
delete(m, "age")
练习应用:统计字符串中每个字符出现次数。
9. 内置函数
常用内置函数一览:
| 函数 | 作用 |
|---|---|
len(v) |
长度 |
cap(v) |
容量 |
make(T, args) |
创建切片/map/通道 |
new(T) |
返回 *T 零值指针 |
append(s, elems) |
追加到切片 |
copy(dst, src) |
复制切片 |
delete(m, k) |
删除 map 元素 |
close(ch) |
关闭通道 |
panic / recover |
异常处理 |
10. 结构体
结构体是字段的集合,可以定义方法。
go
type Book struct {
Title string
Author string
Price float64
}
func (b Book) ShowInfo() {
fmt.Printf("《%s》作者:%s 价格:%.2f\n", b.Title, b.Author, b.Price)
}
func (b *Book) SetPrice(p float64) {
b.Price = p
}
func main() {
book := Book{
Title: "Go语言实战",
Author: "William Kennedy",
Price: 89.0,
}
book.ShowInfo()
book.SetPrice(79.0)
}
方法接收者:值接收者不修改原对象,指针接收者可修改。
11. 函数与方法
- 函数 :支持多返回值、可变参数、匿名函数、闭包、
defer。
go
func div(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
- 方法:与特定类型绑定的函数。
12. 实战练习(附代码思路)
练习1:学生成绩统计
随机生成10个0~100的成绩,计算平均分、最高分、最低分、及格人数以及各分数段人数。
核心代码:
go
// 单次循环完成所有统计
for _, score := range scores {
sum += score
// 最值
if score > max { max = score }
if score < min { min = score }
// 及格
if score >= 60 { passCount++ }
// 分段
switch {
case score >= 90: excellent++
case score >= 80: good++
case score >= 70: medium++
case score >= 60: pass++
default: fail++
}
}
avg := float64(sum) / float64(len(scores))
收获 :掌握了切片遍历、switch 顺序匹配、类型转换。
练习2:字符出现次数统计
输入 "zcddaa",输出 a:2 c:1 d:2 z:1(按字母顺序)。
go
func countAndPrint(s string) {
counts := make(map[rune]int)
for _, ch := range s {
counts[ch]++
}
// 提取键并排序
keys := make([]rune, 0, len(counts))
for k := range counts {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
for i, k := range keys {
if i > 0 {
fmt.Print(" ")
}
fmt.Printf("%c:%d", k, counts[k])
}
}
收获 :map + sort.Slice,理解 rune 和排序。
练习3:数字金字塔
随机生成 1~10 层,打印如下图形(n=5):
text
1
121
12321
1234321
123454321
代码:
go
for i := 1; i <= n; i++ {
// 空格
for j := 0; j < n-i; j++ {
fmt.Print(" ")
}
// 递增
for j := 1; j <= i; j++ {
fmt.Print(j)
}
// 递减
for j := i - 1; j >= 1; j-- {
fmt.Print(j)
}
fmt.Println()
}
收获:嵌套循环、找规律。
练习4:图书管理系统
定义 Book 结构体,创建 3 本书的切片,实现:
-
计算总价的方法
-
找出最贵的书
-
按价格从高到低排序输出
代码片段:
go
type Book struct {
Title string
Author string
Price float64
}
type BookList []Book
func (bl BookList) Total() float64 {
var sum float64
for _, b := range bl {
sum += b.Price
}
return sum
}
func main() {
books := BookList{
{"Go实战", "Kennedy", 89.0},
{"CSAPP", "Bryant", 129.0},
{"算法图解", "Bhargava", 59.5},
}
// 最贵
most := books[0]
for _, b := range books {
if b.Price > most.Price {
most = b
}
}
// 排序
sort.Slice(books, func(i, j int) bool {
return books[i].Price > books[j].Price
})
// 输出...
}
收获 :结构体方法、切片排序、sort.Slice。
13. 常见坑点总结
-
map 必须初始化 :
var m map[string]int是 nil,直接赋值 panic。 -
切片 append 的返回值要接收 :
s = append(s, 1)。 -
for range 的迭代变量是副本:修改值不影响原切片,需用索引。
-
defer 参数在声明时求值,而非调用时。
-
指针不能运算。
14. 后续学习方向
-
并发:goroutine、channel、select
-
接口 interface
-
包管理 go mod
-
标准库:net/http、json、io
-
错误处理最佳实践
15. 结语
一天的时间,我把 Go 的基础语法和几个典型练习过了一遍。Go 的语法非常简洁,没有繁杂的继承和泛型(泛型已支持,但入门先不深究),代码可读性高,编译速度快。
如果你也在学习 Go,希望这篇博客可以作为一个索引,帮你快速回顾核心知识点。代码示例都是可以独立运行的,建议亲手敲一遍,理解会更深刻。
所有练习的完整代码已整理好,欢迎留言讨论。 学完基础,我们就可以开始写简单的 Web 服务器或并发爬虫啦!