Go 语言指针赋值的具体使用
目录
- 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. 总结
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 := &www.devze.comPost{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 javascriptint) *Post { post := Post{ID: id, T编程客栈itle: 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 UpdatePjavascriptostView(post *Post) { post.View++ } // 当不需要修改原始数据时使用值 func GetPostExcerpt(post Post) string { http://www.devze.com 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. 最佳实践
- 一致性:在项目中保持一致的指针使用风格
- 明确意图:使用指针明确表示需要修改数据
- nil 检查:始终检查指针是否为 nil
- 避免过度使用:不要为了使用指针而使用指针
- 文档化:在函数文档中说明参数和返回值的指针语义
10. 总结
Go 语言中的指针赋值是一个强大的特性,但需要谨慎使用:
- 指针赋值:两个指针指向同一块内存
- 值赋值:创建数据的独立副本
- 选择原则:根据数据大小、修改需求和性能要求选择
- 安全第一:始终进行 nil 指针检查
- 保持一致性:在项目中遵循统一的指针使用规范
到此这篇关于Go 语言指针赋值的具体使用的文章就介绍到这了,更多相关Go 语言指针赋值内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论