Go Pointers

A pointer is a variable that stores the memory address of another variable. Go supports pointers but is safer than C/C++ and does not support pointer arithmetic. Understanding pointers is essential for efficient Go programming.

📍 Pointer Basics

Pointer Declaration and Basic Operations

package main

import "fmt"

func main() {
    // 1. 声明变量
    var num int = 42
    var str string = "Hello"
    
    // 2. 声明指针变量
    var numPtr *int    // 指向 int 的指针
    var strPtr *string // 指向 string 的指针
    
    // 3. 获取变量地址(使用 & 操作符)
    numPtr = &num
    strPtr = &str
    
    // 4. 打印地址和值
    fmt.Printf("num 的值: %d\n", num)
    fmt.Printf("num 的地址: %p\n", &num)
    fmt.Printf("numPtr 的值(地址): %p\n", numPtr)
    fmt.Printf("numPtr 指向的值: %d\n", *numPtr)  // 解引用
    
    fmt.Printf("\nstr 的值: %s\n", str)
    fmt.Printf("str 的地址: %p\n", &str)
    fmt.Printf("strPtr 的值(地址): %p\n", strPtr)
    fmt.Printf("strPtr 指向的值: %s\n", *strPtr)
    
    // 5. 零值指针
    var nilPtr *int
    fmt.Printf("\n零值指针: %p\n", nilPtr)  // <nil>
    
    // 6. 检查指针是否为 nil
    if nilPtr == nil {
        fmt.Println("指针为 nil")
    }
}

Pointer Memory Layout

import "unsafe"

func main() {
    // 变量和指针的内存布局
    var x int64 = 100
    var ptr *int64 = &x
    
    fmt.Printf("变量 x:\n")
    fmt.Printf("  值: %d\n", x)
    fmt.Printf("  地址: %p\n", &x)
    fmt.Printf("  大小: %d 字节\n", unsafe.Sizeof(x))
    
    fmt.Printf("\n指针 ptr:\n")
    fmt.Printf("  值(指向的地址): %p\n", ptr)
    fmt.Printf("  自身地址: %p\n", &ptr)
    fmt.Printf("  指向的值: %d\n", *ptr)
    fmt.Printf("  大小: %d 字节\n", unsafe.Sizeof(ptr))
    
    // 修改指针指向的值
    *ptr = 200
    fmt.Printf("\n修改后:\n")
    fmt.Printf("  x 的值: %d\n", x)        // 200
    fmt.Printf("  *ptr 的值: %d\n", *ptr)  // 200
}

🔄 Pointer Operations

Modifying Values Through Pointers

func main() {
    // 基本类型指针
    num := 10
    fmt.Printf("修改前: num = %d\n", num)
    
    modifyValue(&num)
    fmt.Printf("修改后: num = %d\n", num)
    
    // 字符串指针
    message := "Hello"
    fmt.Printf("\n修改前: message = %s\n", message)
    
    modifyString(&message)
    fmt.Printf("修改后: message = %s\n", message)
    
    // 切片指针
    numbers := []int{1, 2, 3}
    fmt.Printf("\n修改前: numbers = %v\n", numbers)
    
    modifySlice(&numbers)
    fmt.Printf("修改后: numbers = %v\n", numbers)
}

func modifyValue(ptr *int) {
    *ptr = *ptr * 2
}

func modifyString(ptr *string) {
    *ptr = *ptr + ", World!"
}

func modifySlice(ptr *[]int) {
    *ptr = append(*ptr, 4, 5, 6)
}

Pointer Passing vs Value Passing

import "fmt"

// 结构体定义
type Person struct {
    Name string
    Age  int
}

func main() {
    person := Person{Name: "Alice", Age: 25}
    
    fmt.Printf("原始: %+v\n", person)
    
    // 值传递:不会修改原始结构体
    modifyPersonByValue(person)
    fmt.Printf("值传递后: %+v\n", person)
    
    // 指针传递:会修改原始结构体
    modifyPersonByPointer(&person)
    fmt.Printf("指针传递后: %+v\n", person)
    
    // 性能对比
    comparePerformance()
}

func modifyPersonByValue(p Person) {
    p.Name = "Bob"
    p.Age = 30
    fmt.Printf("函数内(值传递): %+v\n", p)
}

func modifyPersonByPointer(p *Person) {
    p.Name = "Charlie"  // 等价于 (*p).Name = "Charlie"
    p.Age = 35
    fmt.Printf("函数内(指针传递): %+v\n", *p)
}

func comparePerformance() {
    // 大结构体性能对比
    type LargeStruct struct {
        Data [1000]int
        Info string
    }
    
    large := LargeStruct{Info: "Large data"}
    
    fmt.Printf("\n性能对比:\n")
    fmt.Printf("大结构体大小: %d 字节\n", unsafe.Sizeof(large))
    fmt.Printf("指针大小: %d 字节\n", unsafe.Sizeof(&large))
    
    // 值传递会复制整个结构体
    processByValue(large)
    
    // 指针传递只传递地址
    processByPointer(&large)
}

func processByValue(ls LargeStruct) {
    // 接收复制的结构体
    ls.Info = "Processed by value"
}

func processByPointer(ls *LargeStruct) {
    // 接收指针,直接操作原结构体
    ls.Info = "Processed by pointer"
}

🏗️ Pointers and Data Structures

Array Pointer vs Pointer Array

func main() {
    // 1. 数组指针:指向数组的指针
    arr := [5]int{1, 2, 3, 4, 5}
    var arrPtr *[5]int = &arr
    
    fmt.Printf("原数组: %v\n", arr)
    fmt.Printf("通过数组指针访问: %v\n", *arrPtr)
    
    // 修改数组元素
    arrPtr[0] = 100  // 等价于 (*arrPtr)[0] = 100
    fmt.Printf("修改后数组: %v\n", arr)
    
    // 2. 指针数组:存储指针的数组
    var ptrArr [3]*int
    a, b, c := 10, 20, 30
    ptrArr[0] = &a
    ptrArr[1] = &b
    ptrArr[2] = &c
    
    fmt.Printf("\n指针数组中的值:\n")
    for i, ptr := range ptrArr {
        if ptr != nil {
            fmt.Printf("ptrArr[%d] = %d (地址: %p)\n", i, *ptr, ptr)
        }
    }
    
    // 通过指针数组修改原变量
    *ptrArr[0] = 100
    fmt.Printf("修改后 a = %d\n", a)
}

Slices and Pointers

func main() {
    // 切片本身就包含指针
    slice1 := []int{1, 2, 3, 4, 5}
    slice2 := slice1  // 浅拷贝
    
    fmt.Printf("slice1: %v\n", slice1)
    fmt.Printf("slice2: %v\n", slice2)
    
    // 修改 slice2 会影响 slice1
    slice2[0] = 100
    fmt.Printf("修改 slice2 后:\n")
    fmt.Printf("slice1: %v\n", slice1)
    fmt.Printf("slice2: %v\n", slice2)
    
    // 切片的指针
    var slicePtr *[]int = &slice1
    (*slicePtr)[1] = 200
    fmt.Printf("通过切片指针修改后: %v\n", slice1)
    
    // 函数中操作切片
    processSlice(slice1)
    fmt.Printf("函数处理后: %v\n", slice1)
    
    processSlicePointer(&slice1)
    fmt.Printf("指针函数处理后: %v\n", slice1)
}

func processSlice(s []int) {
    // 可以修改元素,但不能修改长度/容量
    if len(s) > 0 {
        s[0] = 999
    }
}

func processSlicePointer(s *[]int) {
    // 可以修改长度/容量
    *s = append(*s, 6, 7, 8)
}

Maps and Pointers

func main() {
    // 映射的值为指针
    ages := map[string]*int{
        "Alice": new(int),
        "Bob":   new(int),
    }
    
    // 设置值
    *ages["Alice"] = 25
    *ages["Bob"] = 30
    
    fmt.Println("年龄映射:")
    for name, agePtr := range ages {
        if agePtr != nil {
            fmt.Printf("%s: %d岁\n", name, *agePtr)
        }
    }
    
    // 修改值
    *ages["Alice"] = 26
    fmt.Printf("Alice 一年后: %d岁\n", *ages["Alice"])
    
    // 指向映射的指针
    mapPtr := &ages
    (*mapPtr)["Charlie"] = new(int)
    *(*mapPtr)["Charlie"] = 28
    
    fmt.Printf("通过映射指针添加 Charlie: %d岁\n", *ages["Charlie"])
}

🔧 Advanced Pointer Applications

new and make Functions

func main() {
    // 1. new 函数:分配内存并返回指针
    ptr1 := new(int)
    *ptr1 = 42
    fmt.Printf("new 创建的指针: %p, 值: %d\n", ptr1, *ptr1)
    
    ptr2 := new(string)
    *ptr2 = "Hello"
    fmt.Printf("new 创建的字符串指针: %p, 值: %s\n", ptr2, *ptr2)
    
    // 2. make 函数:用于创建切片、映射、通道
    slice := make([]int, 5, 10)
    fmt.Printf("make 创建的切片: %v, 长度: %d, 容量: %d\n", slice, len(slice), cap(slice))
    
    m := make(map[string]int)
    m["key"] = 100
    fmt.Printf("make 创建的映射: %v\n", m)
    
    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    fmt.Printf("make 创建的通道: %v\n", ch)
    
    // 3. new vs make 的区别
    // new 返回指针,make 返回类型本身
    var p1 *[]int = new([]int)      // p1 指向 nil 切片
    var s1 []int = make([]int, 0)   // s1 是空切片
    
    fmt.Printf("new 切片: %v, 是否为 nil: %t\n", *p1, *p1 == nil)
    fmt.Printf("make 切片: %v, 是否为 nil: %t\n", s1, s1 == nil)
}

Pointer Chains and Multi-Level Pointers

func main() {
    // 多级指针
    num := 42
    ptr1 := &num        // 指向 int 的指针
    ptr2 := &ptr1       // 指向指针的指针
    ptr3 := &ptr2       // 指向指针的指针的指针
    
    fmt.Printf("num = %d\n", num)
    fmt.Printf("*ptr1 = %d\n", *ptr1)
    fmt.Printf("**ptr2 = %d\n", **ptr2)
    fmt.Printf("***ptr3 = %d\n", ***ptr3)
    
    // 通过多级指针修改值
    ***ptr3 = 100
    fmt.Printf("修改后 num = %d\n", num)
    
    // 指针链表示例
    type Node struct {
        Data int
        Next *Node
    }
    
    // 创建链表节点
    node1 := &Node{Data: 1}
    node2 := &Node{Data: 2}
    node3 := &Node{Data: 3}
    
    // 连接节点
    node1.Next = node2
    node2.Next = node3
    
    // 遍历链表
    fmt.Println("\n链表遍历:")
    current := node1
    for current != nil {
        fmt.Printf("节点数据: %d\n", current.Data)
        current = current.Next
    }
}

Function Pointers

func main() {
    // 函数指针(函数作为值)
    var mathOp func(int, int) int
    
    mathOp = add
    fmt.Printf("加法: %d\n", mathOp(5, 3))
    
    mathOp = multiply
    fmt.Printf("乘法: %d\n", mathOp(5, 3))
    
    // 函数指针数组
    operations := []func(int, int) int{add, subtract, multiply, divide}
    operationNames := []string{"加法", "减法", "乘法", "除法"}
    
    a, b := 10, 3
    for i, op := range operations {
        result := op(a, b)
        fmt.Printf("%s: %d\n", operationNames[i], result)
    }
    
    // 回调函数
    processNumbers([]int{1, 2, 3, 4, 5}, func(n int) int {
        return n * n
    })
}

func add(a, b int) int      { return a + b }
func subtract(a, b int) int { return a - b }
func multiply(a, b int) int { return a * b }
func divide(a, b int) int   { return a / b }

func processNumbers(numbers []int, processor func(int) int) {
    fmt.Println("处理数字:")
    for _, num := range numbers {
        result := processor(num)
        fmt.Printf("%d -> %d\n", num, result)
    }
}

🎯 Practical Examples

Linked List Data Structure

// 单向链表实现
type LinkedList struct {
    head *ListNode
    size int
}

type ListNode struct {
    data int
    next *ListNode
}

func NewLinkedList() *LinkedList {
    return &LinkedList{}
}

func (ll *LinkedList) Add(data int) {
    newNode := &ListNode{data: data}
    
    if ll.head == nil {
        ll.head = newNode
    } else {
        current := ll.head
        for current.next != nil {
            current = current.next
        }
        current.next = newNode
    }
    ll.size++
}

func (ll *LinkedList) Remove(data int) bool {
    if ll.head == nil {
        return false
    }
    
    // 如果要删除的是头节点
    if ll.head.data == data {
        ll.head = ll.head.next
        ll.size--
        return true
    }
    
    current := ll.head
    for current.next != nil {
        if current.next.data == data {
            current.next = current.next.next
            ll.size--
            return true
        }
        current = current.next
    }
    return false
}

func (ll *LinkedList) Find(data int) *ListNode {
    current := ll.head
    for current != nil {
        if current.data == data {
            return current
        }
        current = current.next
    }
    return nil
}

func (ll *LinkedList) Display() {
    if ll.head == nil {
        fmt.Println("链表为空")
        return
    }
    
    current := ll.head
    fmt.Print("链表: ")
    for current != nil {
        fmt.Printf("%d", current.data)
        if current.next != nil {
            fmt.Print(" -> ")
        }
        current = current.next
    }
    fmt.Printf(" (大小: %d)\n", ll.size)
}

func main() {
    // 测试链表
    list := NewLinkedList()
    
    // 添加元素
    list.Add(1)
    list.Add(2)
    list.Add(3)
    list.Add(4)
    list.Display()
    
    // 查找元素
    node := list.Find(3)
    if node != nil {
        fmt.Printf("找到元素: %d\n", node.data)
    }
    
    // 删除元素
    if list.Remove(2) {
        fmt.Println("删除元素 2 成功")
        list.Display()
    }
    
    // 删除头节点
    if list.Remove(1) {
        fmt.Println("删除头节点 1 成功")
        list.Display()
    }
}

Binary Tree Data Structure

// 二叉搜索树实现
type TreeNode struct {
    data  int
    left  *TreeNode
    right *TreeNode
}

type BinarySearchTree struct {
    root *TreeNode
}

func NewBST() *BinarySearchTree {
    return &BinarySearchTree{}
}

func (bst *BinarySearchTree) Insert(data int) {
    bst.root = insertNode(bst.root, data)
}

func insertNode(node *TreeNode, data int) *TreeNode {
    if node == nil {
        return &TreeNode{data: data}
    }
    
    if data < node.data {
        node.left = insertNode(node.left, data)
    } else if data > node.data {
        node.right = insertNode(node.right, data)
    }
    
    return node
}

func (bst *BinarySearchTree) Search(data int) bool {
    return searchNode(bst.root, data)
}

func searchNode(node *TreeNode, data int) bool {
    if node == nil {
        return false
    }
    
    if data == node.data {
        return true
    } else if data < node.data {
        return searchNode(node.left, data)
    } else {
        return searchNode(node.right, data)
    }
}

func (bst *BinarySearchTree) InorderTraversal() {
    fmt.Print("中序遍历: ")
    inorder(bst.root)
    fmt.Println()
}

func inorder(node *TreeNode) {
    if node != nil {
        inorder(node.left)
        fmt.Printf("%d ", node.data)
        inorder(node.right)
    }
}

func main() {
    // 测试二叉搜索树
    bst := NewBST()
    
    // 插入节点
    values := []int{50, 30, 20, 40, 70, 60, 80}
    for _, val := range values {
        bst.Insert(val)
        fmt.Printf("插入 %d\n", val)
    }
    
    // 中序遍历(应该是有序的)
    bst.InorderTraversal()
    
    // 搜索节点
    searchValues := []int{40, 25, 80, 90}
    for _, val := range searchValues {
        found := bst.Search(val)
        fmt.Printf("搜索 %d: %t\n", val, found)
    }
}

🎓 Summary

In this chapter, we comprehensively learned about Go pointers:

  • Pointer basics: declaration, address retrieval, dereferencing
  • Pointer operations: modifying values through pointers, pointer passing
  • Data structures: array pointers, slice pointers, map pointers
  • Advanced applications: new/make, multi-level pointers, function pointers
  • Practical applications: linked lists, binary trees, and other data structures

Pointers are an important feature of Go. Using them correctly can improve program performance and enable complex data structures.


Next, we will learn about Go Structs to understand Go's custom data types.

::: tip Pointer Usage Tips

  • Use pointers for large structs as function parameters to avoid copy overhead
  • Always check if pointers are nil to avoid runtime panics
  • Understand the difference between pointers and values, and choose the appropriate passing method
  • Use pointers reasonably to implement efficient data structures :::