
本文深入探讨Go语言中*和&这两个核心操作符的作用。&用于获取变量的内存地址,生成一个指向该变量的指针;而*则用于声明指针类型、对指针进行解引用以访问其指向的值,以及通过指针间接修改变量的值。理解它们对于掌握Go的内存管理和数据传递机制至关重要,尤其是在函数参数传递和结构体操作中。
在go语言中,指针是一个核心概念,它允许程序直接操作内存地址,从而实现更灵活的数据管理和高效的程序设计。理解*(星号)和&(取地址符)这两个操作符是掌握go语言指针的关键。
1. & 操作符:取地址(Address-of Operator)
& 操作符用于获取一个变量的内存地址。当应用于一个变量时,它会返回该变量在内存中的地址,这个地址就是一个指向该变量类型的新指针。
语法: &variable
示例:
package mainimport "fmt"func main() { value := 100 ptr := &value // ptr 现在是一个指向 value 变量的指针 fmt.Printf("变量 value 的值:%dn", value) fmt.Printf("变量 value 的内存地址:%pn", ptr) // %p 用于打印指针地址}
在上述示例中,ptr 变量存储的不是 100 这个值,而是 value 变量在内存中的具体位置。
立即学习“go语言免费学习笔记(深入)”;
2. * 操作符:指针的声明、解引用与间接赋值
* 操作符在Go语言中扮演了多重角色,其具体含义取决于它出现的位置。
2.1 指针类型声明(Pointer Type Declaration)
当 * 紧跟在一个类型前面时(例如 *int, *string),它表示声明一个指向该类型数据的指针。这意味着这个变量将存储一个内存地址,而这个地址指向的是指定类型的数据。
语法: var variableName *Type
示例:
package mainimport "fmt"func main() { var p *int // 声明 p 是一个指向 int 类型数据的指针 fmt.Printf("p 的零值(未初始化):%vn", p) // 指针的零值是 nil}
2.2 指针解引用(Dereferencing)
当 * 放置在一个已声明的指针变量前面时,它被称为解引用操作符。解引用操作用于访问或获取指针所指向的内存地址中存储的实际值。
语法: *pointerVariable
示例:
package mainimport "fmt"func main() { value := 200 ptr := &value // ptr 指向 value 的地址 fmt.Printf("通过 ptr 解引用获取 value 的值:%dn", *ptr) // *ptr 获取 ptr 指向的值}
2.3 指针间接赋值(Indirect Assignment)
通过解引用操作符,我们不仅可以读取指针指向的值,还可以修改该值。当 *pointerVariable 出现在赋值语句的左侧时,它表示修改指针所指向内存地址中的值。
语法: *pointerVariable = newValue
示例:
package mainimport "fmt"func main() { num := 300 ptr := &num // ptr 指向 num 的地址 fmt.Printf("修改前 num 的值:%dn", num) *ptr = 400 // 通过 ptr 间接修改 num 的值 fmt.Printf("修改后 num 的值:%dn", num)}
3. 综合示例分析
为了更深入地理解 & 和 * 的协同工作,我们来分析一个典型的代码片段:
package mainimport ( "fmt" "os")func main() { s := "hello" // 声明并初始化字符串变量 s if s[1] != 'e' { // 检查字符串 s 的第二个字符是否为 'e' os.Exit(1) // 如果不是,则退出程序 } fmt.Printf("s 初始值: %s, 地址: %pn", s, &s) s = "good bye" // 重新赋值 s fmt.Printf("s 重新赋值后: %s, 地址: %pn", s, &s) var p *string = &s // 声明一个字符串指针 p,并用 s 的地址初始化它 fmt.Printf("指针 p 的值 (s 的地址): %pn", p) fmt.Printf("通过指针 p 解引用得到的值: %sn", *p) *p = "ciao" // 通过指针 p 间接修改 s 的值 fmt.Printf("通过指针 p 修改后 s 的值: %sn", s) fmt.Printf("通过指针 p 修改后,通过 p 解引用得到的值: %sn", *p)}
分析过程:
s := “hello”:变量 s 被创建并赋值为 “hello”。s = “good bye”:s 的值被更新为 “good bye”。此时 s 的内存地址保持不变,但其内容已更改。var p *string = &s:&s:获取变量 s 的内存地址。*string:声明 p 是一个指向 string 类型的指针。=:将 s 的内存地址赋值给 p。现在,p 指向 s 所在的内存位置。*p = “ciao”:*p:解引用 p,表示访问 p 所指向的内存位置(即 s 的内存位置)。=:将 “ciao” 这个字符串赋值到 p 所指向的内存位置。这意味着 s 的值被间接修改为 “ciao”。
运行此代码,您会观察到 s 的值最终变为 “ciao”,这证明了通过指针可以实现对原始变量的间接修改。
4. Go语言中的“引用”与“值传递”
Go语言中所有函数参数的传递都是值传递(pass by value)。这意味着当您将一个变量作为参数传递给函数时,函数会接收到该变量的一个副本。对副本的任何修改都不会影响原始变量。
然而,当您传递一个指针的值时,函数接收到的是原始变量的内存地址的副本。虽然这个地址本身是一个副本,但它指向的仍然是原始变量在内存中的位置。因此,通过这个指针副本解引用并修改其指向的值,就能够影响到原始变量。这在效果上类似于其他语言中的“引用传递”,但本质上仍是值传递——只是传递的是一个地址值。
package mainimport "fmt"func modifyValue(x int) { // 值传递,x 是 num 的副本 x = 20}func modifyValueByPointer(ptr *int) { // 值传递,ptr 是 &num 的副本 *ptr = 20 // 通过指针修改 num}func main() { num := 10 fmt.Println("初始值:", num) // 10 modifyValue(num) fmt.Println("值传递后:", num) // 10 (未改变) modifyValueByPointer(&num) fmt.Println("指针传递后:", num) // 20 (已改变)}
5. 指针的应用场景与注意事项
5.1 典型应用场景
修改函数外部变量:如上例所示,当需要在函数内部修改函数外部的变量时,传递变量的指针是标准做法。避免大数据结构复制:当结构体或数组非常大时,将其作为参数传递会导致整个数据结构的复制,消耗大量内存和时间。传递其指针可以避免这种开销,提高性能。实现链表、树等数据结构:这些数据结构天然依赖于节点间的指针连接。方法接收者:Go语言中,方法的接收者可以是值类型也可以是指针类型。使用指针接收者可以修改接收者实例的字段。
5.2 注意事项
零值指针(nil):未初始化的指针变量的零值是 nil。尝试解引用 nil 指针会导致运行时错误(panic)。在使用指针前,务必确保其已被正确初始化或检查是否为 nil。Go不支持指针算术:与C/C++不同,Go语言不允许对指针进行加减运算来访问相邻内存地址。这是为了提高内存安全性和代码可读性。垃圾回收:Go语言拥有自动垃圾回收机制,开发者无需手动管理内存的分配和释放。当一个内存地址不再有任何指针指向它时,垃圾回收器会在适当的时候自动回收这块内存。
总结
& 和 * 是Go语言中操作指针的两个基本且强大的操作符。& 用于获取变量的内存地址,生成一个指针;而 * 则用于声明指针类型、解引用指针以访问其指向的值,以及通过指针间接修改变量的值。理解并熟练运用它们,是编写高效、安全Go程序的基础。掌握指针不仅能让您更深入地理解Go的内存模型,也能帮助您在特定场景下优化程序性能和实现复杂的数据结构。
以上就是Go语言中指针操作符*与取地址符&的全面解析的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1394051.html
微信扫一扫
支付宝扫一扫