This commit is contained in:
2026-03-14 19:03:12 +08:00
parent 2805379586
commit 6d86d62825
34 changed files with 3213 additions and 0 deletions
@@ -0,0 +1,60 @@
# 01-pointers - 指针基础
## 知识点讲解
### 指针声明
```go
var p *int // 声明一个int类型的指针
x := 10
p = &x // p是x的地址
```
### 指针操作
```go
x := 10
p := &x // 取地址
fmt.Println(*p) // 解引用,输出10
*p = 20 // 通过指针修改值
fmt.Println(x) // 输出20
```
### 指针与函数
```go
function modifyValue(p *int) {
*p = 100 // 修改指针指向的值
}
x := 10
modifyValue(&x) // 传递地址
fmt.Println(x) // 输出100
```
### 零值
```go
var p *int // nil指针
fmt.Println(p == nil) // true
```
### 重要提示
- Go中有指针但没有指针运算
- 指针可以用于共享数据、避免拷贝
- 指针的零值是nil
- 解引用nil指针会panic
## 学习目标
完成本练习后,你将:
- ✅ 掌握指针的声明和创建
- ✅ 学会取地址和解引用操作
- ✅ 理解指针与函数参数的关系
- ✅ 了解nil指针的概念
## 练习说明
打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。
@@ -0,0 +1,96 @@
package main
import "fmt"
func exercise1() int {
fmt.Println("=== 练习1:指针声明 ===")
// 声明一个int变量和一个指向它的指针
var x int = 10
var p ____ // 声明指针类型
p = ____ // p指向x的地址
fmt.Printf("x的值: %d, x的地址: %p\n", x, p)
fmt.Printf("*p的值: %d\n", ____)
return ____
}
func exercise2() int {
fmt.Println("=== 练习2:指针解引用 ===")
// 通过指针修改值
x := 20
p := ____
____ = 30
fmt.Println("修改后x的值:", x)
return ____
}
func modifyByPointer(p *int) {
// 通过指针修改值
____ = 100
}
func exercise3() int {
fmt.Println("=== 练习3:指针作为函数参数 ===")
// 使用指针作为函数参数
x := 50
____(&x)
if x != 100 {
fmt.Println("指针参数修改失败!")
}
return ____
}
func exercise4() bool {
fmt.Println("=== 练习4:指针零值 ===")
// 检查指针的零值
var p ____
fmt.Printf("未初始化的指针: %v, 是否为nil: %t\n", p, ____)
return ____
}
func exercise5() int {
fmt.Println("=== 练习5:指针与数组 ===")
// 使用指针访问数组元素
arr := [5]int{10, 20, 30, 40, 50}
p := ____
fmt.Printf("第一个元素: %d\n", ____)
fmt.Printf("第二个元素: %d\n", ____(p+1))
return arr[0]
}
func main() {
fmt.Println("=== 指针 练习 ===\n")
result1 := exercise1()
fmt.Printf("练习1结果: %d\n", result1)
result2 := exercise2()
fmt.Printf("练习2结果: %d\n", result2)
result3 := exercise3()
fmt.Printf("练习3结果: %d\n", result3)
result4 := exercise4()
fmt.Printf("练习4结果: %t\n", result4)
result5 := exercise5()
fmt.Printf("练习5结果: %d\n", result5)
}
@@ -0,0 +1,77 @@
package main
import (
"bytes"
"os"
"strings"
"testing"
)
func captureOutput(f func()) string {
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
f()
w.Close()
os.Stdout = old
var buf bytes.Buffer
buf.ReadFrom(r)
return buf.String()
}
func TestExercise1(t *testing.T) {
result := exercise1()
if result != 10 {
t.Errorf("期望 10, 实际: %d", result)
}
}
func TestExercise2(t *testing.T) {
result := exercise2()
if result != 30 {
t.Errorf("期望 30, 实际: %d", result)
}
}
func TestExercise3(t *testing.T) {
result := exercise3()
if result != 100 {
t.Errorf("期望 100, 实际: %d", result)
}
}
func TestExercise4(t *testing.T) {
result := exercise4()
if !result {
t.Errorf("期望 true, 实际: %t", result)
}
}
func TestExercise5(t *testing.T) {
result := exercise5()
if result != 10 {
t.Errorf("期望 10, 实际: %d", result)
}
}
func TestMainFunction(t *testing.T) {
output := captureOutput(main)
requiredSections := []string{
"=== 指针 练习 ===",
"练习1:指针声明",
"练习2:指针解引用",
"练习3:指针作为函数参数",
"练习4:指针零值",
"练习5:指针与数组",
}
for _, section := range requiredSections {
if !strings.Contains(output, section) {
t.Errorf("期望输出包含 '%s'", section)
}
}
}
@@ -0,0 +1,63 @@
# 02-new-make - new vs make
## 知识点讲解
### new函数
```go
p := new(int) // 返回指向int类型零值的指针
fmt.Println(*p) // 输出: 0
*p = 10 // 可以通过指针修改值
```
**特点**:
- 返回指向类型的零值指针
- 适用于所有类型
- 内存已初始化为零值但不执行构造逻辑
### make函数
```go
// 创建切片
s := make([]int, 5) // [0, 0, 0, 0, 0]
s := make([]int, 3, 10) // 长度3, 容量10
// 创建map
m := make(map[string]int)
m["key"] = 100
// 创建channel
ch := make(chan int)
```
**特点**:
- 只用于创建引用类型(slice、map、channel)
- 返回的是初始化后的类型(非指针)
- 会执行类型的初始化逻辑
### 主要区别
| 特性 | new() | make() |
|------|-------|--------|
| 返回类型 | 指针 | 值 |
| 适用类型 | 所有类型 | slice, map, channel |
| 初始化 | 仅零值 | 完整初始化 |
| 常用场景 | 需要指针时 | 创建引用类型时 |
### 重要提示
- 使用 `new` 创建的指针指向零值
- 使用 `make` 创建的引用类型可以直接使用
- 不要用 `new` 创建切片、map或channel
## 学习目标
完成本练习后,你将:
- ✅ 理解new和make的区别
- ✅ 掌握new的使用场景
- ✅ 掌握make的使用场景
- ✅ 学会正确选择new或make
## 练习说明
打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。
+102
View File
@@ -0,0 +1,102 @@
package main
import "fmt"
func exercise1() int {
fmt.Println("=== 练习1:new创建指针 ===")
// 使用new创建一个int类型的指针
p := ____ // new返回指向int零值的指针
fmt.Printf("new创建的指针: %v, 解引用值: %d\n", p, ____)
*p = 10
fmt.Printf("修改后的值: %d\n", ____)
return ____
}
func exercise2() []int {
fmt.Println("=== 练习2:make创建切片 ===")
// 使用make创建一个长度为5的int切片
s := ____
fmt.Printf("创建的切片: %v, 长度: %d\n", s, ____)
fmt.Printf("切片的第一个元素: %d\n", s[____])
return ____
}
func exercise3() map[string]int {
fmt.Println("=== 练习3:make创建map ===")
// 使用make创建一个map
m := ____
m["张三"] = 90
fmt.Printf("map内容: %v\n", ____)
return ____
}
func exercise4() int {
fmt.Println("=== 练习4:new vs make - 切片 ===")
// 分别使用new和make创建切片,观察区别
newSlice := new([]int) // 返回指针,指向nil切片
makeSlice := make([]int, 3) // 返回切片实例
fmt.Printf("new创建的切片: %v, 是否为nil: %t\n", ____, *newSlice == nil)
fmt.Printf("make创建的切片: %v, 是否为nil: %t\n", ____, makeSlice == nil)
if *newSlice == nil {
*newSlice = make([]int, 3)
____[0] = 100
}
return ____
}
func exercise5() string {
fmt.Println("=== 练习5:new vs make - 结构体 ===")
// 使用new创建结构体指针
type Person struct {
Name string
Age int
}
p := ____
fmt.Printf("new创建的结构体: %+v\n", ____)
p.Name = "张三"
p.Age = 20
fmt.Printf("修改后的结构体: %+v\n", ____)
return ____
}
func main() {
fmt.Println("=== new vs make 练习 ===\n")
result1 := exercise1()
fmt.Printf("练习1结果: %d\n", result1)
result2 := exercise2()
fmt.Printf("练习2结果: %v\n", result2)
result3 := exercise3()
fmt.Printf("练习3结果: %v\n", result3)
result4 := exercise4()
fmt.Printf("练习4结果: %d\n", result4)
result5 := exercise5()
fmt.Printf("练习5结果: %s\n", result5)
}
@@ -0,0 +1,77 @@
package main
import (
"bytes"
"os"
"strings"
"testing"
)
func captureOutput(f func()) string {
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
f()
w.Close()
os.Stdout = old
var buf bytes.Buffer
buf.ReadFrom(r)
return buf.String()
}
func TestExercise1(t *testing.T) {
result := exercise1()
if result != 10 {
t.Errorf("期望 10, 实际: %d", result)
}
}
func TestExercise2(t *testing.T) {
result := exercise2()
if len(result) != 5 {
t.Errorf("期望长度 5, 实际: %d", len(result))
}
}
func TestExercise3(t *testing.T) {
result := exercise3()
if len(result) != 1 {
t.Errorf("期望长度 1, 实际: %d", len(result))
}
}
func TestExercise4(t *testing.T) {
result := exercise4()
if result != 100 {
t.Errorf("期望 100, 实际: %d", result)
}
}
func TestExercise5(t *testing.T) {
result := exercise5()
if result != "张三" {
t.Errorf("期望 '张三', 实际: %s", result)
}
}
func TestMainFunction(t *testing.T) {
output := captureOutput(main)
requiredSections := []string{
"=== new vs make 练习 ===",
"练习1:new创建指针",
"练习2:make创建切片",
"练习3:make创建map",
"练习4:new vs make - 切片",
"练习5:new vs make - 结构体",
}
for _, section := range requiredSections {
if !strings.Contains(output, section) {
t.Errorf("期望输出包含 '%s'", section)
}
}
}
@@ -0,0 +1,75 @@
# 03-value-pointer - 值传递vs指针传递
## 知识点讲解
### 值传递
```go
func modifyValue(x int) {
x = 100 // 修改的是函数内的副本
}
num := 10
modifyValue(num)
fmt.Println(num) // 输出: 10(原值不变)
```
### 指针传递
```go
func modifyPointer(p *int) {
*p = 100 // 修改的是指针指向的值
}
num := 10
modifyPointer(&num)
fmt.Println(num) // 输出: 100(原值被修改)
```
### 结构体的值传递
```go
type Person struct {
Name string
Age int
}
func modifyPersonValue(p Person) {
p.Name = "李四" // 修改副本
}
p := Person{Name: "张三", Age: 20}
modifyPersonValue(p)
fmt.Println(p.Name) // 输出: 张三(原值不变)
```
### 结构体的指针传递
```go
func modifyPersonPointer(p *Person) {
p.Name = "李四" // 修改原值
}
p := Person{Name: "张三", Age: 20}
modifyPersonPointer(&p)
fmt.Println(p.Name) // 输出: 李四(原值被修改)
```
### 重要提示
- Go中所有参数传递都是值传递
- 传递指针实际是拷贝指针地址,但通过指针可以修改原值
- 大结构体使用指针传递可以避免大量拷贝
- 切片、map等引用类型传递也具有类似指针的行为
## 学习目标
完成本练习后,你将:
- ✅ 理解值传递和指针传递的区别
- ✅ 掌握何时使用值传递
- ✅ 掌握何时使用指针传递
- ✅ 了解性能方面的考虑
## 练习说明
打开 `main.go`,根据注释提示填充代码,完成后运行 `go test -v` 验证答案。
@@ -0,0 +1,130 @@
package main
import "fmt"
func modifyByValue(x int) int {
// 值传递:修改函数内的副本
x = ____
return ____
}
func modifyByPointer(p *int) int {
// 指针传递:修改指针指向的值
____ = ____
return ____
}
type Counter struct {
Value int
}
func (c Counter) Increment() int {
// 值接收者:修改副本
c.Value += 1
return ____
}
func (c *Counter) IncrementPointer() int {
// 指针接收者:修改原值
c.Value += ____
return ____
}
func exercise1() int {
fmt.Println("=== 练习1:值传递 ===")
// 值传递不会修改原值
num := 10
result := modifyByValue(____)
fmt.Printf("原值: %d, 函数返回值: %d\n", ____, result)
return ____
}
func exercise2() int {
fmt.Println("=== 练习2:指针传递 ===")
// 指针传递会修改原值
num := 10
result := ____
fmt.Printf("原值: %d, 函数返回值: %d\n", ____, result)
return ____
}
func exercise3() int {
fmt.Println("=== 练习3:结构体值传递 ===")
// 值方法不会修改原结构体
counter := Counter{Value: 0}
for i := 0; i < 5; i++ {
counter.____()
}
fmt.Printf("原结构体值: %d\n", ____)
return ____
}
func exercise4() int {
fmt.Println("=== 练习4:结构体指针传递 ===")
// 指针方法会修改原结构体
counter := Counter{Value: 0}
for i := 0; i < 5; i++ {
counter.____()
}
fmt.Printf("原结构体值: %d\n", ____)
return ____
}
type SliceContainer struct {
Data []int
}
func (s *SliceContainer) Add(value int) {
// 指针方法可以修改切片内容
s.Data = append(____, ____)
}
func exercise5() []int {
fmt.Println("=== 练习5:切片与结构体 ===")
// 使用切片的结构体
container := SliceContainer{Data: []int{}}
container.Add(10)
container.Add(20)
container.Add(30)
fmt.Printf("切片内容: %v\n", ____)
return ____
}
func main() {
fmt.Println("=== 值传递 vs 指针传递 练习 ===\n")
result1 := exercise1()
fmt.Printf("练习1结果: %d\n", result1)
result2 := exercise2()
fmt.Printf("练习2结果: %d\n", result2)
result3 := exercise3()
fmt.Printf("练习3结果: %d\n", result3)
result4 := exercise4()
fmt.Printf("练习4结果: %d\n", result4)
result5 := exercise5()
fmt.Printf("练习5结果: %v\n", result5)
}
@@ -0,0 +1,83 @@
package main
import (
"bytes"
"os"
"strings"
"testing"
)
func captureOutput(f func()) string {
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
f()
w.Close()
os.Stdout = old
var buf bytes.Buffer
buf.ReadFrom(r)
return buf.String()
}
func TestExercise1(t *testing.T) {
result := exercise1()
if result != 10 {
t.Errorf("期望 10, 实际: %d", result)
}
}
func TestExercise2(t *testing.T) {
result := exercise2()
if result != 100 {
t.Errorf("期望 100, 实际: %d", result)
}
}
func TestExercise3(t *testing.T) {
result := exercise3()
if result != 0 {
t.Errorf("期望 0, 实际: %d", result)
}
}
func TestExercise4(t *testing.T) {
result := exercise4()
if result != 5 {
t.Errorf("期望 5, 实际: %d", result)
}
}
func TestExercise5(t *testing.T) {
result := exercise5()
expected := []int{10, 20, 30}
if len(result) != len(expected) {
t.Errorf("期望长度 %d, 实际: %d", len(expected), len(result))
}
for i, val := range expected {
if result[i] != val {
t.Errorf("索引 %d 期望 %d, 实际: %d", i, val, result[i])
}
}
}
func TestMainFunction(t *testing.T) {
output := captureOutput(main)
requiredSections := []string{
"=== 值传递 vs 指针传递 练习 ===",
"练习1:值传递",
"练习2:指针传递",
"练习3:结构体值传递",
"练习4:结构体指针传递",
"练习5:切片与结构体",
}
for _, section := range requiredSections {
if !strings.Contains(output, section) {
t.Errorf("期望输出包含 '%s'", section)
}
}
}