Go语言指针赋值的具体使用

Written by

in

文章目录
  • 一致性:在项目中保持一致的指针使用风格 明确意图:使用指针明确表示需要修改数据 nil 检查:始终检查指针是否为 nil 避免过度使用:不要为了使用指针而使用指针 文档化:在函数文档中说明参数和返回值的指针语义
  • Go 语言中的指针赋值是一个强大的特性,但需要谨慎使用: 指针赋值:两个指针指向同一块内存 值赋值:创建数据的独立副本 选择原则:根据数据大小、修改需求和性能要求选择 安全第一:始终进行 nil 指针检查 保持一致性:在项目中遵循统一的指针使用规范 到此这篇关于Go 语言指针赋值的具体使用的文章就介绍到这了,更多相关Go 语言指针赋值内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!  您可能感兴趣的文章: Go语言的方法接受者类型用值类型还是指针类型? Go语言指针访问结构体的方法 Go语言中结构体方法副本传参与指针传参的区别介绍 go语言结构体指针操作示例详解 Golang语言如何避免空指针引发的panic详解 深入理解Golang中指针的用途与技巧 Go语言什么时候该使用指针 一文介绍Go语言中的指针 golang双指针快速排序的实现代码
  • 目录
    • 1. 基本概念
      • 指针是什么?
      • 基本语法
    • 2. 指针声明和初始化
      • 2.1 声明指针
      • 2.2 结构体指针
      • 2.3 等价性
    • 3. 指针赋值操作
      • 3.1 基本赋值
      • 3.2 切片中的指针赋值
      • 3.3 函数参数中的指针
    • 4. 指针的特殊情况
      • 4.1 指针的零值
      • 4.2 返回指针
      • 4.3 多重指针
    • 5. 指针数组和数组指针
      • 5.1 指针数组
      • 5.2 数组指针
    • 6. 常见陷阱和注意事项
      • 6.1 循环中的指针问题
      • 6.2 nil 指针解引用
      • 6.3 返回局部变量地址
    • 7. 实际应用场景
      • 7.1 数据库操作(如项目中的使用)
      • 7.2 函数参数传递
      • 7.3 切片和映射中的指针
    • 8. 性能考虑
      • 8.1 何时使用指针
      • 8.2 何时使用值
    • 9. 最佳实践
      • 10. 总结

        指针是存储另一个变量内存地址的变量。在 Go 中,指针用 * 符号表示。

        var ptr *int    // 声明一个指向 int 的指针
        ptr = &value    // 将 value 的地址赋值给 ptr
        *ptr = 100      // 通过指针修改值
        

        // 方式1: 先声明,后赋值
        var ptr *int
        var value int = 42
        ptr = &value
        
        // 方式2: 声明时初始化
        ptr := &value
        
        // 方式3: 使用 new 函数
        ptr := new(int)
        *ptr = 42
        

        type Post struct {
            ID    uint
            Title string
            View  int
        }
        
        // 方式1: 先创建结构体,再取地址
        post := Post{ID: 1, Title: "文章1", View: 100}
        postPtr := &post
        
        // 方式2: 直接创建结构体指针
        postPtr := &Post{ID: 1, Title: "文章1", View: 100}
        
        // 方式3: 使用 new 函数
        postPtr := new(Post)
        postPtr.ID = 1
        postPtr.Title = "文章1"
        postPtr.View = 100
        

        person := &Person{Name: "John", Age: 20}
        
        // 这两种写法完全等价:
        (*person).Name = "Jane"  // 显式解引用
        person.Name = "Jane"     // 隐式解引用(语法糖)
        
        // 你写的代码
        person.Name = "Jane"
        
        // 编译器实际生成的代码
        (*person).Name = "Jane"
        

        var num int = 42
        ptr1 := &num
        ptr2 := ptr1  // ptr2 现在指向与 ptr1 相同的内存地址
        
        // 修改其中一个指针指向的值,另一个也会改变
        *ptr1 = 100
        fmt.Println(*ptr2) // 输出: 100
        

        // 指针切片
        posts := []*Post{
            {ID: 1, Title: "文章1", View: 10},
            {ID: 2, Title: "文章2", View: 20},
        }
        
        // 获取切片中元素的指针
        firstPostPtr := posts[0]
        firstPostPtr.Title = "修改后的文章1"  // 这会修改原始数据
        

        func modifyPost(post *Post) {
            post.Title = "修改后的标题"  // 修改会影响原始数据
        }
        
        post := Post{ID: 1, Title: "原始标题", View: 0}
        modifyPost(&post)  // 传递地址
        fmt.Println(post.Title) // 输出: "修改后的标题"
        

        var ptr *Post  // ptr 的零值是 nil
        fmt.Println(ptr) // 输出: <nil>
        
        // 安全的 nil 指针检查
        if ptr != nil {
            fmt.Println(ptr.Title)
        } else {
            fmt.Println("指针是 nil")
        }
        

        func createPost(id uint, title string, view int) *Post {
            post := Post{ID: id, Title: title, View: view}
            return &post  // Go 编译器会优化,不会返回栈上变量的地址
        }
        
        postPtr := createPost(1, "新文章", 100)
        

        value := 42
        ptr := &value
        ptrToPtr := &ptr
        
        fmt.Printf("value = %dn", value)           // 42
        fmt.Printf("*ptr = %dn", *ptr)             // 42
        fmt.Printf("**ptrToPtr = %dn", **ptrToPtr) // 42
        

        // 指针数组:数组中的每个元素都是指针
        numbers := []int{1, 2, 3}
        pointerArray := [3]*int{&numbers[0], &numbers[1], &numbers[2]}
        
        // 修改指针指向的值
        *pointerArray[0] = 100
        fmt.Println(numbers[0]) // 输出: 100
        

        // 数组指针:指向整个数组的指针
        numbers := [3]int{1, 2, 3}
        arrayPointer := &numbers
        
        // 通过数组指针访问元素
        fmt.Println(arrayPointer[0]) // 输出: 1
        fmt.Println((*arrayPointer)[0]) // 等价写法
        

        // ❌ 错误的方式:所有指针都指向同一个变量
        numbers := []int{1, 2, 3}
        var pointers []*int
        for _, num := range numbers {
            pointers = append(pointers, &num) // 错误!所有指针都指向循环变量
        }
        
        // ✅ 正确的方式:为每个值创建独立的指针
        var correctPointers []*int
        for i := range numbers {
            correctPointers = append(correctPointers, &numbers[i])
        }
        

        var ptr *Post
        // fmt.Println(ptr.Title) // 这会导致 panic
        
        // 安全的访问方式
        if ptr != nil {
            fmt.Println(ptr.Title)
        }
        

        // 概念上不推荐,但 Go 编译器会优化
        func badPractice() *Post {
            post := Post{ID: 1, Title: "局部变量", View: 100}
            return &post // Go 编译器会优化,不会返回栈上变量的地址
        }
        

        func GetPostById(id uint) (*Post, error) {
            var post Post
            err := DB.First(&post, "id = ?", id).Error
            return &post, err  // 返回指针,避免大型结构体的复制
        }
        

        // 当需要修改原始数据时使用指针
        func UpdatePostView(post *Post) {
            post.View++
        }
        
        // 当不需要修改原始数据时使用值
        func GetPostExcerpt(post Post) string {
            return post.Title[:10] + "..."
        }
        

        // 指针切片:共享数据
        posts := []*Post{
            {ID: 1, Title: "文章1", View: 10},
            {ID: 2, Title: "文章2", View: 20},
        }
        
        // 修改会影响原始数据
        posts[0].Title = "修改后的文章1"
        
        // 值切片:独立副本
        postValues := []Post{
            {ID: 1, Title: "文章1", View: 10},
            {ID: 2, Title: "文章2", View: 20},
        }
        
        // 修改不会影响原始数据
        postValues[0].Title = "修改后的文章1"
        

        • 大型结构体:避免复制开销
        • 需要修改原始数据:函数内部修改需要影响调用者
        • 接口实现:某些接口要求指针接收者

        • 小型结构体:复制开销小
        • 不需要修改原始数据:函数式编程风格
        • 需要数据隔离:确保数据不被意外修改

        1. 一致性:在项目中保持一致的指针使用风格
        2. 明确意图:使用指针明确表示需要修改数据
        3. nil 检查:始终检查指针是否为 nil
        4. 避免过度使用:不要为了使用指针而使用指针
        5. 文档化:在函数文档中说明参数和返回值的指针语义

        Go 语言中的指针赋值是一个强大的特性,但需要谨慎使用:

        • 指针赋值:两个指针指向同一块内存
        • 值赋值:创建数据的独立副本
        • 选择原则:根据数据大小、修改需求和性能要求选择
        • 安全第一:始终进行 nil 指针检查
        • 保持一致性:在项目中遵循统一的指针使用规范

        到此这篇关于Go 语言指针赋值的具体使用的文章就介绍到这了,更多相关Go 语言指针赋值内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 

        您可能感兴趣的文章:

        • Go语言的方法接受者类型用值类型还是指针类型?
        • Go语言指针访问结构体的方法
        • Go语言中结构体方法副本传参与指针传参的区别介绍
        • go语言结构体指针操作示例详解
        • Golang语言如何避免空指针引发的panic详解
        • 深入理解Golang中指针的用途与技巧
        • Go语言什么时候该使用指针
        • 一文介绍Go语言中的指针
        • golang双指针快速排序的实现代码

        站内搜索