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

 更新时间:2025年08月08日 10:27:37   作者:太凉  
Go语言中指针用于存储变量地址,可提升性能但需谨慎使用,需注意nil检查、避免循环问题及返回局部变量地址的陷阱,下面就来介绍一下指针赋值,感兴趣的可以了解一下

1. 基本概念

指针是什么?

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

基本语法

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

2. 指针声明和初始化

2.1 声明指针

// 方式1: 先声明,后赋值
var ptr *int
var value int = 42
ptr = &value

// 方式2: 声明时初始化
ptr := &value

// 方式3: 使用 new 函数
ptr := new(int)
*ptr = 42

2.2 结构体指针

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

2.3 等价性

person := &Person{Name: "John", Age: 20}

// 这两种写法完全等价:
(*person).Name = "Jane"  // 显式解引用
person.Name = "Jane"     // 隐式解引用(语法糖)

// 你写的代码
person.Name = "Jane"

// 编译器实际生成的代码
(*person).Name = "Jane"

3. 指针赋值操作

3.1 基本赋值

var num int = 42
ptr1 := &num
ptr2 := ptr1  // ptr2 现在指向与 ptr1 相同的内存地址

// 修改其中一个指针指向的值,另一个也会改变
*ptr1 = 100
fmt.Println(*ptr2) // 输出: 100

3.2 切片中的指针赋值

// 指针切片
posts := []*Post{
    {ID: 1, Title: "文章1", View: 10},
    {ID: 2, Title: "文章2", View: 20},
}

// 获取切片中元素的指针
firstPostPtr := posts[0]
firstPostPtr.Title = "修改后的文章1"  // 这会修改原始数据

3.3 函数参数中的指针

func modifyPost(post *Post) {
    post.Title = "修改后的标题"  // 修改会影响原始数据
}

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

4. 指针的特殊情况

4.1 指针的零值

var ptr *Post  // ptr 的零值是 nil
fmt.Println(ptr) // 输出: <nil>

// 安全的 nil 指针检查
if ptr != nil {
    fmt.Println(ptr.Title)
} else {
    fmt.Println("指针是 nil")
}

4.2 返回指针

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

postPtr := createPost(1, "新文章", 100)

4.3 多重指针

value := 42
ptr := &value
ptrToPtr := &ptr

fmt.Printf("value = %d\n", value)           // 42
fmt.Printf("*ptr = %d\n", *ptr)             // 42
fmt.Printf("**ptrToPtr = %d\n", **ptrToPtr) // 42

5. 指针数组和数组指针

5.1 指针数组

// 指针数组:数组中的每个元素都是指针
numbers := []int{1, 2, 3}
pointerArray := [3]*int{&numbers[0], &numbers[1], &numbers[2]}

// 修改指针指向的值
*pointerArray[0] = 100
fmt.Println(numbers[0]) // 输出: 100

5.2 数组指针

// 数组指针:指向整个数组的指针
numbers := [3]int{1, 2, 3}
arrayPointer := &numbers

// 通过数组指针访问元素
fmt.Println(arrayPointer[0]) // 输出: 1
fmt.Println((*arrayPointer)[0]) // 等价写法

6. 常见陷阱和注意事项

6.1 循环中的指针问题

// ❌ 错误的方式:所有指针都指向同一个变量
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])
}

6.2 nil 指针解引用

var ptr *Post
// fmt.Println(ptr.Title) // 这会导致 panic

// 安全的访问方式
if ptr != nil {
    fmt.Println(ptr.Title)
}

6.3 返回局部变量地址

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

7. 实际应用场景

7.1 数据库操作(如项目中的使用)

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

7.2 函数参数传递

// 当需要修改原始数据时使用指针
func UpdatePostView(post *Post) {
    post.View++
}

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

7.3 切片和映射中的指针

// 指针切片:共享数据
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"

8. 性能考虑

8.1 何时使用指针

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

8.2 何时使用值

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

9. 最佳实践

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

10. 总结

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

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

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

相关文章

  • golang http请求未释放造成的错误问题

    golang http请求未释放造成的错误问题

    这篇文章主要介绍了golang http请求未释放造成的错误问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Go语言开发框架反射机制及常见函数示例详解

    Go语言开发框架反射机制及常见函数示例详解

    这篇文章主要为大家介绍了Go语言开发框架反射机制及常见函数示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • golang处理TIFF图像的实现示例

    golang处理TIFF图像的实现示例

    本文介绍了在Go语言中处理TIFF图像,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-03-03
  • Go语言格式化动词使用详解

    Go语言格式化动词使用详解

    这篇文章主要介绍了Go语言格式化动词使用详解的相关资料,需要的朋友可以参考下
    2023-08-08
  • Go 字符串比较的实现示例

    Go 字符串比较的实现示例

    本文主要介绍了Go 字符串比较的实现示例,主要包括三种比较方式,具有一定的参考价值,感兴趣的可以了解一下
    2022-01-01
  • 详解如何通过Go来操作Redis实现简单的读写操作

    详解如何通过Go来操作Redis实现简单的读写操作

    作为最常用的分布式缓存中间件——Redis,了解运作原理和如何使用是十分有必要的,今天来学习如何通过Go来操作Redis实现基本的读写操作,需要的朋友可以参考下
    2023-09-09
  • 基于Go语言实现一个并发下载器

    基于Go语言实现一个并发下载器

    这篇文章主要为大家详细介绍了如何利用GO语言实现一个并发的文件下载器,可以在不重新启动整个下载的情况下处理错误,感兴趣的小伙伴可以了解一下
    2023-10-10
  • Golang自定义结构体转map的操作

    Golang自定义结构体转map的操作

    这篇文章主要介绍了Golang自定义结构体转map的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 关于Go 空结构体的 3 种使用场景

    关于Go 空结构体的 3 种使用场景

    在今天这篇文章要给大家介绍得是Go 语言中几种常见类型的宽度,并且基于开头的问题 ”空结构体“ 进行了剖析,需要的朋友可以参考一下,希望对你有所帮助
    2021-10-10
  • 一文带你掌握GoLang中的指针

    一文带你掌握GoLang中的指针

    针是 Go 编程语言的重要组成部分,它们提供了一种直接引用和操作内存中数据的方法,在这篇文章中,我们将探讨一些技巧和技巧,以掌握 GoLang 中的指针并编写高效的代码,需要的可以参考一下
    2023-05-05

最新评论