/* 05-pointers.go - Go 语言指针详解 学习目标: 1. 理解指针的概念和作用 2. 掌握指针的声明和使用 3. 学会指针的运算和操作 4. 了解指针与函数参数的关系 5. 掌握指针的实际应用场景 知识点: - 指针的定义和特性 - 指针的声明和初始化 - 取地址操作符 & - 解引用操作符 * - 指针与函数参数 - 指针与结构体 - 指针的零值 nil - 指针的安全使用 */ package main import ( "fmt" ) func main() { fmt.Println("=== Go 语言指针详解 ===\n") // 演示指针的基本概念 demonstrateBasicPointers() // 演示指针的操作 demonstratePointerOperations() // 演示指针与函数 demonstratePointersWithFunctions() // 演示指针与结构体 demonstratePointersWithStructs() // 演示指针与数组/切片 demonstratePointersWithArraysSlices() // 演示指针的高级用法 demonstrateAdvancedPointerUsage() // 演示指针的实际应用 demonstratePracticalApplications() } // demonstrateBasicPointers 演示指针的基本概念 func demonstrateBasicPointers() { fmt.Println("1. 指针的基本概念:") // 指针的基本特性 fmt.Printf(" 指针的基本特性:\n") fmt.Printf(" - 存储变量的内存地址\n") fmt.Printf(" - 通过地址间接访问变量\n") fmt.Printf(" - 零值是 nil\n") fmt.Printf(" - 类型安全,不支持指针运算\n") fmt.Printf(" - 自动垃圾回收管理\n") // 基本指针示例 fmt.Printf(" 基本指针示例:\n") // 声明变量 var x int = 42 fmt.Printf(" 变量 x: %d\n", x) fmt.Printf(" x 的地址: %p\n", &x) // 声明指针 var p *int fmt.Printf(" 指针 p 的零值: %v\n", p) fmt.Printf(" p == nil: %t\n", p == nil) // 指针赋值 p = &x fmt.Printf(" p = &x 后:\n") fmt.Printf(" p 的值(地址): %p\n", p) fmt.Printf(" p 指向的值: %d\n", *p) fmt.Printf(" p 的地址: %p\n", &p) // 通过指针修改值 *p = 100 fmt.Printf(" *p = 100 后:\n") fmt.Printf(" x 的值: %d\n", x) fmt.Printf(" *p 的值: %d\n", *p) // 指针的类型 fmt.Printf(" 指针的类型:\n") var intPtr *int var floatPtr *float64 var stringPtr *string fmt.Printf(" *int 指针: %T\n", intPtr) fmt.Printf(" *float64 指针: %T\n", floatPtr) fmt.Printf(" *string 指针: %T\n", stringPtr) fmt.Println() } // demonstratePointerOperations 演示指针的操作 func demonstratePointerOperations() { fmt.Println("2. 指针的操作:") // 取地址操作符 & fmt.Printf(" 取地址操作符 &:\n") a := 10 b := 20 c := 30 fmt.Printf(" 变量地址:\n") fmt.Printf(" &a: %p\n", &a) fmt.Printf(" &b: %p\n", &b) fmt.Printf(" &c: %p\n", &c) // 解引用操作符 * fmt.Printf(" 解引用操作符 *:\n") ptr := &a fmt.Printf(" ptr 指向 a: %p\n", ptr) fmt.Printf(" *ptr: %d\n", *ptr) // 修改指针指向 ptr = &b fmt.Printf(" ptr 指向 b: %p\n", ptr) fmt.Printf(" *ptr: %d\n", *ptr) // 指针赋值 fmt.Printf(" 指针赋值:\n") var p1, p2 *int p1 = &a p2 = p1 // 指针赋值,两个指针指向同一地址 fmt.Printf(" p1: %p, *p1: %d\n", p1, *p1) fmt.Printf(" p2: %p, *p2: %d\n", p2, *p2) fmt.Printf(" p1 == p2: %t\n", p1 == p2) // 通过不同指针修改同一变量 *p1 = 99 fmt.Printf(" *p1 = 99 后:\n") fmt.Printf(" a: %d\n", a) fmt.Printf(" *p2: %d\n", *p2) // nil 指针检查 fmt.Printf(" nil 指针检查:\n") var nilPtr *int fmt.Printf(" nilPtr: %v\n", nilPtr) fmt.Printf(" nilPtr == nil: %t\n", nilPtr == nil) // 安全的指针使用 if nilPtr != nil { fmt.Printf(" *nilPtr: %d\n", *nilPtr) } else { fmt.Printf(" nilPtr 是 nil,不能解引用\n") } // 指针比较 fmt.Printf(" 指针比较:\n") x, y := 1, 2 px, py := &x, &y px2 := &x fmt.Printf(" px == py: %t (指向不同变量)\n", px == py) fmt.Printf(" px == px2: %t (指向同一变量)\n", px == px2) fmt.Printf(" px == nil: %t\n", px == nil) fmt.Println() } // demonstratePointersWithFunctions 演示指针与函数 func demonstratePointersWithFunctions() { fmt.Println("3. 指针与函数:") // 值传递 vs 指针传递 fmt.Printf(" 值传递 vs 指针传递:\n") x := 10 fmt.Printf(" 原始值: %d\n", x) // 值传递 modifyByValue(x) fmt.Printf(" 值传递后: %d (未改变)\n", x) // 指针传递 modifyByPointer(&x) fmt.Printf(" 指针传递后: %d (已改变)\n", x) // 函数返回指针 fmt.Printf(" 函数返回指针:\n") ptr := createInt(42) fmt.Printf(" 返回的指针: %p\n", ptr) fmt.Printf(" 指针指向的值: %d\n", *ptr) // 多个返回值包含指针 value, valuePtr := createIntPair(100) fmt.Printf(" 返回值: %d\n", value) fmt.Printf(" 返回指针: %p, 值: %d\n", valuePtr, *valuePtr) // 指针作为函数参数的优势 fmt.Printf(" 指针作为函数参数的优势:\n") // 交换变量 a, b := 5, 10 fmt.Printf(" 交换前: a=%d, b=%d\n", a, b) swap(&a, &b) fmt.Printf(" 交换后: a=%d, b=%d\n", a, b) // 避免大结构体拷贝 fmt.Printf(" 避免大结构体拷贝:\n") person := Person{Name: "Alice", Age: 25, Email: "alice@example.com"} fmt.Printf(" 修改前: %+v\n", person) // 使用指针避免结构体拷贝 updatePersonByPointer(&person, 26) fmt.Printf(" 指针修改后: %+v\n", person) fmt.Println() } // demonstratePointersWithStructs 演示指针与结构体 func demonstratePointersWithStructs() { fmt.Println("4. 指针与结构体:") // 结构体指针的创建 fmt.Printf(" 结构体指针的创建:\n") // 方式1: 先创建结构体,再取地址 person1 := Person{Name: "Alice", Age: 25, Email: "alice@example.com"} personPtr1 := &person1 fmt.Printf(" 方式1: %+v\n", *personPtr1) // 方式2: 直接创建结构体指针 personPtr2 := &Person{Name: "Bob", Age: 30, Email: "bob@example.com"} fmt.Printf(" 方式2: %+v\n", *personPtr2) // 方式3: 使用 new 函数 personPtr3 := new(Person) personPtr3.Name = "Charlie" personPtr3.Age = 35 personPtr3.Email = "charlie@example.com" fmt.Printf(" 方式3: %+v\n", *personPtr3) // 结构体指针的字段访问 fmt.Printf(" 结构体指针的字段访问:\n") // 方式1: 显式解引用 fmt.Printf(" (*personPtr1).Name: %s\n", (*personPtr1).Name) // 方式2: 自动解引用(推荐) fmt.Printf(" personPtr1.Name: %s\n", personPtr1.Name) // 修改结构体字段 fmt.Printf(" 修改结构体字段:\n") fmt.Printf(" 修改前: %s\n", personPtr1.Name) personPtr1.Name = "Alice Smith" fmt.Printf(" 修改后: %s\n", personPtr1.Name) fmt.Printf(" 原结构体: %s\n", person1.Name) // 也被修改了 // 结构体指针切片 fmt.Printf(" 结构体指针切片:\n") people := []*Person{ {Name: "David", Age: 28, Email: "david@example.com"}, {Name: "Eve", Age: 32, Email: "eve@example.com"}, {Name: "Frank", Age: 29, Email: "frank@example.com"}, } fmt.Printf(" 人员列表:\n") for i, person := range people { fmt.Printf(" %d: %s (%d岁)\n", i+1, person.Name, person.Age) } // 修改切片中的结构体 people[0].Age = 29 fmt.Printf(" 修改后第一个人的年龄: %d\n", people[0].Age) // 结构体指针映射 fmt.Printf(" 结构体指针映射:\n") userMap := map[string]*Person{ "alice": {Name: "Alice", Age: 25, Email: "alice@example.com"}, "bob": {Name: "Bob", Age: 30, Email: "bob@example.com"}, } if user, exists := userMap["alice"]; exists { fmt.Printf(" 找到用户: %+v\n", *user) user.Age = 26 // 修改映射中的结构体 fmt.Printf(" 修改后: %+v\n", *user) } fmt.Println() } // demonstratePointersWithArraysSlices 演示指针与数组/切片 func demonstratePointersWithArraysSlices() { fmt.Println("5. 指针与数组/切片:") // 数组指针 fmt.Printf(" 数组指针:\n") arr := [5]int{1, 2, 3, 4, 5} arrPtr := &arr fmt.Printf(" 数组: %v\n", arr) fmt.Printf(" 数组指针: %p\n", arrPtr) fmt.Printf(" 通过指针访问: %v\n", *arrPtr) // 修改数组元素 arrPtr[2] = 99 // 等同于 (*arrPtr)[2] = 99 fmt.Printf(" 修改后数组: %v\n", arr) // 元素指针 fmt.Printf(" 元素指针:\n") slice := []int{10, 20, 30, 40, 50} fmt.Printf(" 切片: %v\n", slice) // 获取元素指针 elemPtr := &slice[2] fmt.Printf(" slice[2] 的地址: %p\n", elemPtr) fmt.Printf(" slice[2] 的值: %d\n", *elemPtr) // 通过元素指针修改 *elemPtr = 300 fmt.Printf(" 修改后切片: %v\n", slice) // 指针切片 fmt.Printf(" 指针切片:\n") var intPtrs []*int for i := range slice { intPtrs = append(intPtrs, &slice[i]) } fmt.Printf(" 指针切片长度: %d\n", len(intPtrs)) fmt.Printf(" 第一个指针指向的值: %d\n", *intPtrs[0]) // 通过指针切片修改原切片 *intPtrs[1] = 200 fmt.Printf(" 修改后原切片: %v\n", slice) // 数组指针 vs 指针数组 fmt.Printf(" 数组指针 vs 指针数组:\n") // 数组指针:指向数组的指针 var arrayPtr *[3]int = &[3]int{1, 2, 3} fmt.Printf(" 数组指针类型: %T\n", arrayPtr) fmt.Printf(" 数组指针值: %v\n", *arrayPtr) // 指针数组:元素是指针的数组 x, y, z := 1, 2, 3 var ptrArray [3]*int = [3]*int{&x, &y, &z} fmt.Printf(" 指针数组类型: %T\n", ptrArray) fmt.Printf(" 指针数组值: [%d, %d, %d]\n", *ptrArray[0], *ptrArray[1], *ptrArray[2]) fmt.Println() }// demons trateAdvancedPointerUsage 演示指针的高级用法 func demonstrateAdvancedPointerUsage() { fmt.Println("6. 指针的高级用法:") // 双重指针 fmt.Printf(" 双重指针:\n") x := 42 ptr := &x ptrPtr := &ptr fmt.Printf(" x: %d\n", x) fmt.Printf(" ptr: %p, *ptr: %d\n", ptr, *ptr) fmt.Printf(" ptrPtr: %p, *ptrPtr: %p, **ptrPtr: %d\n", ptrPtr, *ptrPtr, **ptrPtr) // 通过双重指针修改 **ptrPtr = 100 fmt.Printf(" **ptrPtr = 100 后, x: %d\n", x) // 指针与接口 fmt.Printf(" 指针与接口:\n") var shape Shape // 值类型实现接口 circle := Circle{Radius: 5} shape = circle fmt.Printf(" 圆形面积: %.2f\n", shape.Area()) // 指针类型实现接口 rect := &Rectangle{Width: 4, Height: 3} shape = rect fmt.Printf(" 矩形面积: %.2f\n", shape.Area()) // 指针与方法集 fmt.Printf(" 指针与方法集:\n") counter := Counter{Value: 0} fmt.Printf(" 初始计数器: %+v\n", counter) // 值类型调用指针接收者方法(自动取地址) counter.Increment() fmt.Printf(" 调用 Increment() 后: %+v\n", counter) // 指针类型调用值接收者方法(自动解引用) counterPtr := &Counter{Value: 10} value := counterPtr.GetValue() fmt.Printf(" 指针调用值方法: %d\n", value) // unsafe 包的使用(谨慎使用) fmt.Printf(" unsafe 包的使用:\n") var num int64 = 0x1234567890ABCDEF fmt.Printf(" 原始值: 0x%X\n", num) // 获取指针 numPtr := unsafe.Pointer(&num) // 将 int64 指针转换为 int32 指针(低32位) lowPtr := (*int32)(numPtr) fmt.Printf(" 低32位: 0x%X\n", *lowPtr) // 获取高32位 highPtr := (*int32)(unsafe.Pointer(uintptr(numPtr) + 4)) fmt.Printf(" 高32位: 0x%X\n", *highPtr) // 指针大小 fmt.Printf(" 指针大小:\n") fmt.Printf(" 指针大小: %d 字节\n", unsafe.Sizeof(ptr)) fmt.Printf(" uintptr 大小: %d 字节\n", unsafe.Sizeof(uintptr(0))) fmt.Println() } // demonstratePracticalApplications 演示指针的实际应用 func demonstratePracticalApplications() { fmt.Println("7. 指针的实际应用:") // 应用1: 链表实现 fmt.Printf(" 应用1 - 链表实现:\n") list := &LinkedList{} // 添加节点 list.Add(1) list.Add(2) list.Add(3) fmt.Printf(" 链表内容: ") list.Print() // 查找节点 if list.Contains(2) { fmt.Printf(" 链表包含值 2\n") } // 删除节点 list.Remove(2) fmt.Printf(" 删除 2 后: ") list.Print() // 应用2: 树结构 fmt.Printf(" 应用2 - 二叉树:\n") tree := &BinaryTree{} // 插入节点 tree.Insert(5) tree.Insert(3) tree.Insert(7) tree.Insert(1) tree.Insert(9) fmt.Printf(" 中序遍历: ") tree.InorderTraversal() fmt.Printf("\n") // 查找节点 if tree.Search(7) { fmt.Printf(" 树中包含值 7\n") } // 应用3: 缓存系统 fmt.Printf(" 应用3 - LRU 缓存:\n") cache := NewLRUCache(3) // 添加缓存项 cache.Put("a", 1) cache.Put("b", 2) cache.Put("c", 3) fmt.Printf(" 缓存状态: ") cache.Print() // 访问缓存项 if value, found := cache.Get("b"); found { fmt.Printf(" 获取 'b': %d\n", value) } // 添加新项(会淘汰最久未使用的) cache.Put("d", 4) fmt.Printf(" 添加 'd' 后: ") cache.Print() // 应用4: 对象池 fmt.Printf(" 应用4 - 对象池:\n") pool := NewObjectPool() // 获取对象 obj1 := pool.Get() obj2 := pool.Get() fmt.Printf(" 获取对象1: %p\n", obj1) fmt.Printf(" 获取对象2: %p\n", obj2) // 使用对象 obj1.Data = "Hello" obj2.Data = "World" fmt.Printf(" 对象1数据: %s\n", obj1.Data) fmt.Printf(" 对象2数据: %s\n", obj2.Data) // 归还对象 pool.Put(obj1) pool.Put(obj2) // 再次获取(可能复用之前的对象) obj3 := pool.Get() fmt.Printf(" 再次获取对象: %p (数据: %s)\n", obj3, obj3.Data) // 应用5: 回调函数 fmt.Printf(" 应用5 - 回调函数:\n") processor := &DataProcessor{} // 设置回调函数 processor.SetCallback(func(data string) { fmt.Printf(" 处理数据: %s\n", data) }) // 处理数据 processor.Process("Hello, World!") // 应用6: 观察者模式 fmt.Printf(" 应用6 - 观察者模式:\n") subject := &Subject{} // 添加观察者 observer1 := &Observer{Name: "Observer1"} observer2 := &Observer{Name: "Observer2"} subject.AddObserver(observer1) subject.AddObserver(observer2) // 通知观察者 subject.NotifyObservers("重要消息") fmt.Println() } // ========== 类型定义 ========== // 基本结构体 type Person struct { Name string Age int Email string } // 几何图形接口 type Shape interface { Area() float64 } // 圆形 type Circle struct { Radius float64 } func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius } // 矩形 type Rectangle struct { Width float64 Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } // 计数器 type Counter struct { Value int } func (c *Counter) Increment() { c.Value++ } func (c Counter) GetValue() int { return c.Value } // 链表节点 type ListNode struct { Value int Next *ListNode } // 链表 type LinkedList struct { Head *ListNode } func (l *LinkedList) Add(value int) { newNode := &ListNode{Value: value} if l.Head == nil { l.Head = newNode } else { current := l.Head for current.Next != nil { current = current.Next } current.Next = newNode } } func (l *LinkedList) Contains(value int) bool { current := l.Head for current != nil { if current.Value == value { return true } current = current.Next } return false } func (l *LinkedList) Remove(value int) { if l.Head == nil { return } if l.Head.Value == value { l.Head = l.Head.Next return } current := l.Head for current.Next != nil { if current.Next.Value == value { current.Next = current.Next.Next return } current = current.Next } } func (l *LinkedList) Print() { current := l.Head for current != nil { fmt.Printf("%d ", current.Value) current = current.Next } fmt.Printf("\n") } // 二叉树节点 type TreeNode struct { Value int Left *TreeNode Right *TreeNode } // 二叉树 type BinaryTree struct { Root *TreeNode } func (t *BinaryTree) Insert(value int) { t.Root = t.insertNode(t.Root, value) } func (t *BinaryTree) insertNode(node *TreeNode, value int) *TreeNode { if node == nil { return &TreeNode{Value: value} } if value < node.Value { node.Left = t.insertNode(node.Left, value) } else { node.Right = t.insertNode(node.Right, value) } return node } func (t *BinaryTree) Search(value int) bool { return t.searchNode(t.Root, value) } func (t *BinaryTree) searchNode(node *TreeNode, value int) bool { if node == nil { return false } if value == node.Value { return true } else if value < node.Value { return t.searchNode(node.Left, value) } else { return t.searchNode(node.Right, value) } } func (t *BinaryTree) InorderTraversal() { t.inorderNode(t.Root) } func (t *BinaryTree) inorderNode(node *TreeNode) { if node != nil { t.inorderNode(node.Left) fmt.Printf("%d ", node.Value) t.inorderNode(node.Right) } } // LRU 缓存节点 type CacheNode struct { Key string Value int Prev *CacheNode Next *CacheNode } // LRU 缓存 type LRUCache struct { Capacity int Cache map[string]*CacheNode Head *CacheNode Tail *CacheNode } func NewLRUCache(capacity int) *LRUCache { head := &CacheNode{} tail := &CacheNode{} head.Next = tail tail.Prev = head return &LRUCache{ Capacity: capacity, Cache: make(map[string]*CacheNode), Head: head, Tail: tail, } } func (c *LRUCache) Get(key string) (int, bool) { if node, exists := c.Cache[key]; exists { c.moveToHead(node) return node.Value, true } return 0, false } func (c *LRUCache) Put(key string, value int) { if node, exists := c.Cache[key]; exists { node.Value = value c.moveToHead(node) } else { newNode := &CacheNode{Key: key, Value: value} c.Cache[key] = newNode c.addToHead(newNode) if len(c.Cache) > c.Capacity { tail := c.removeTail() delete(c.Cache, tail.Key) } } } func (c *LRUCache) addToHead(node *CacheNode) { node.Prev = c.Head node.Next = c.Head.Next c.Head.Next.Prev = node c.Head.Next = node } func (c *LRUCache) removeNode(node *CacheNode) { node.Prev.Next = node.Next node.Next.Prev = node.Prev } func (c *LRUCache) moveToHead(node *CacheNode) { c.removeNode(node) c.addToHead(node) } func (c *LRUCache) removeTail() *CacheNode { lastNode := c.Tail.Prev c.removeNode(lastNode) return lastNode } func (c *LRUCache) Print() { current := c.Head.Next for current != c.Tail { fmt.Printf("(%s:%d) ", current.Key, current.Value) current = current.Next } fmt.Printf("\n") } // 对象池 type PoolObject struct { Data string } type ObjectPool struct { pool []*PoolObject } func NewObjectPool() *ObjectPool { return &ObjectPool{ pool: make([]*PoolObject, 0), } } func (p *ObjectPool) Get() *PoolObject { if len(p.pool) > 0 { obj := p.pool[len(p.pool)-1] p.pool = p.pool[:len(p.pool)-1] return obj } return &PoolObject{} } func (p *ObjectPool) Put(obj *PoolObject) { obj.Data = "" // 重置对象 p.pool = append(p.pool, obj) } // 数据处理器 type DataProcessor struct { callback func(string) } func (d *DataProcessor) SetCallback(callback func(string)) { d.callback = callback } func (d *DataProcessor) Process(data string) { if d.callback != nil { d.callback(data) } } // 观察者模式 type Observer struct { Name string } func (o *Observer) Update(message string) { fmt.Printf(" %s 收到消息: %s\n", o.Name, message) } type Subject struct { observers []*Observer } func (s *Subject) AddObserver(observer *Observer) { s.observers = append(s.observers, observer) } func (s *Subject) NotifyObservers(message string) { for _, observer := range s.observers { observer.Update(message) } } // ========== 辅助函数 ========== // 值传递函数 func modifyByValue(x int) { x = 999 fmt.Printf(" 函数内部: %d\n", x) } // 指针传递函数 func modifyByPointer(x *int) { *x = 888 fmt.Printf(" 函数内部: %d\n", *x) } // 返回指针的函数 func createInt(value int) *int { x := value // 局部变量 return &x // 返回局部变量的地址(Go 会自动分配到堆上) } // 返回值和指针 func createIntPair(value int) (int, *int) { x := value return x, &x } // 交换函数 func swap(a, b *int) { *a, *b = *b, *a } // 更新结构体 func updatePersonByPointer(p *Person, newAge int) { p.Age = newAge } /* 运行这个程序: go run 05-pointers.go 学习要点: 1. 指针存储变量的内存地址,通过地址间接访问变量 2. 使用 & 获取地址,使用 * 解引用指针 3. 指针的零值是 nil,使用前要检查 4. 指针传参可以避免大对象拷贝,允许函数修改原变量 5. Go 的指针是类型安全的,不支持指针运算 指针的特性: 1. 类型安全:不同类型的指针不能相互赋值 2. 自动管理:垃圾回收器自动管理内存 3. 无指针运算:不支持 C 风格的指针运算 4. 自动解引用:结构体指针可以直接访问字段 5. 方法调用:自动在值和指针间转换 指针的用途: 1. 避免大对象拷贝 2. 允许函数修改参数 3. 实现数据结构(链表、树等) 4. 共享数据 5. 可选值表示 指针与函数: 1. 值传递:传递变量的拷贝 2. 指针传递:传递变量的地址 3. 返回指针:可以返回局部变量的地址 4. 方法接收者:值接收者 vs 指针接收者 最佳实践: 1. 使用前检查指针是否为 nil 2. 大结构体使用指针传递 3. 需要修改参数时使用指针 4. 合理使用指针避免内存泄漏 5. 优先使用值类型,必要时使用指针 常见应用场景: 1. 数据结构实现 2. 对象池和缓存 3. 回调函数 4. 观察者模式 5. 链式数据结构 6. 可选参数 注意事项: 1. nil 指针解引用会 panic 2. 指针比较比较的是地址 3. 指针可能导致内存泄漏 4. 避免返回栈变量的指针(Go 会自动处理) 5. 理解值接收者和指针接收者的区别 unsafe 包: 1. 提供底层内存操作 2. 绕过类型安全检查 3. 谨慎使用,可能破坏内存安全 4. 主要用于系统编程和性能优化 */