Go变量指针的实现示例
作者:Jayden_念旧
Go指针存储变量地址,用于修改变量、避免拷贝、动态分配,本文就来详细的介绍一下Go变量指针的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
在Go语言中,指针是一个存储变量内存地址的变量。通过指针,可以间接访问和操作原始数据。从而提高程序的效率,特别是在处理复杂数据结构或需要共享数据时。
概念 | 说明 |
---|---|
指针声明 | var p *int |
取地址 | &a → 获取 a 的地址 |
解引用 | *p → 访问 p 指向的值 |
零值 | nil |
主要用途 | 修改变量、避免拷贝、动态分配 |
1.指针的本质
- 指针是一种特殊变量,其值是另一个变量的内存地址。
- 通过指针可以间接访问或修改原始变量的值。
2.声明指针
语法:var ptr *Type
var a int = 10 var ptr *int // 声明一个指向 int 类型的指针 ptr = &a // 将 a 的内存地址赋值给 ptr
3.关键操作符
&
(取地址符):获取变量的内存地址。
ptr = &a // ptr 现在存储 a 的地址
*
(解引用符):访问指针指向的原始值。
fmt.Println(*ptr) // 输出 10(通过 ptr 访问 a 的值) *ptr = 20 // 修改 a 的值为 20
4.指针的零值
- 未初始化的指针值为
nil
(表示无指向)。
var ptr *int // 初始值为 nil
5.指针的用途
- 修改函数外部的变量:
func modify(val *int) { *val = 100 } func main() { x := 10 modify(&x) // x 被修改为 100 }
- 避免大对象拷贝:传递结构体指针而非整个结构体,减少内存开销。
type Data struct { /* 大结构体 */ } func process(d *Data) { /* 操作 d */ }
- 动态内存分配:与
new
或复合字面量一起使用。
p := new(int) // 分配内存,返回指针 *p = 42
6.指针示例
package main import ( "fmt" ) type Person struct { Name string Age int Address string Salary float64 } func NewPerson(name string, age int, address string, salary float64) *Person { return &Person{Name: name, Age: age, Address: address, Salary: salary} } func main() { // 创建Person实例 p := NewPerson("张三", 30, "北京市海淀区", 15000.50) // 访问字段 fmt.Println(p.Name) // 输出:张三 fmt.Println(p.Salary) // 输出:15000.5 // 修改字段值 p.Age = 31 p.Salary = 16000.00 }
为什么使用创建结构体指针:
1.内存效率优化
// 值类型返回(产生完整结构体拷贝) func NewPerson() Person { return Person{...} } // 指针类型返回(仅传递内存地址) func NewPerson() *Person { return &Person{...} }
- 结构体大小:当结构体包含多个字段(特别是包含大数组等)时,指针传递(通常 8 字节)比值拷贝更高效
- 调用链损耗:在方法调用链中多次传递时,指针传递始终维持固定内存开销
2. 可变性控制
p := NewPerson("李四", 28, "上海", 25000) p.Age = 29 // 直接修改原结构体 // 值类型接收者方法示例(无法修改原结构体) func (p Person) SetAge(age int) { p.Age = age // 仅修改副本 } // 指针类型接收者方法示例 func (p *Person) RealSetAge(age int) { p.Age = age // 修改原结构体 }
- 指针接收者:通过返回指针,后续可以方便地定义指针接收者方法
- 状态一致性:确保所有方法操作都作用于同一个内存对象
3. 零值有效性
// 错误处理扩展性 func NewPerson(age int) (*Person, error) { if age < 0 { return nil, errors.New("invalid age") } return &Person{Age: age}, nil }
- nil 语义:当构造函数需要实现错误处理时,指针类型可以直接返回 nil
- 可选对象:调用方可通过
if p != nil
判断对象是否有效
7.注意!!!!!!!
- 空指针(nil):解引用
nil
指针会导致运行时 panic。 - 指针算术:Go 不支持指针算术(如
ptr++
),除非使用unsafe
包(不推荐)。 - 值传递 vs 指针传递:函数参数默认值传递,需用指针实现引用传递。
扩展:使用指针修改原始变量和通过函数修改的区别是什么
原理差异
- 指针修改:在 Go 里,字符串是不可变类型,若要通过指针修改字符串,实际上修改的是存储字符串的字节切片。因为字符串底层由字节数组构成,借助指针操作可改变字节切片内容,再转换回字符串,从而更新原始变量。
strings.Builder
修改:strings.Builder
内部维护一个字节切片,提供方法将字符串追加到该切片。当调用String
方法时,会把内部字节切片转换为字符串返回。这种方式不会直接修改原始字符串,而是生成新字符串后重新赋值给原始变量。
代码实现差异
指针修改示例
package main import ( "fmt" ) func modifyString(s *string) { bytes := []byte(*s) bytes = append(bytes, ' ', 'a', 'b', 'c') *s = string(bytes) } func main() { str := "this is a string" fmt.Println(&str) modifyString(&str) fmt.Println(str) }
strings.Builder修改示例
package main import ( "fmt" "strings" ) func main() { str := "this is a string" fmt.Println(&str) // 创建一个 strings.Builder 实例 var builder strings.Builder // 将原始字符串写入 Builder builder.WriteString(str) // 在末尾添加 abc builder.WriteString(" abc") // 获取最终的字符串 str = builder.String() fmt.Println(str) }
性能差异
- 指针修改:每次修改都要将字符串转换为字节切片,修改后再转换回字符串,频繁转换会带来额外内存分配和复制开销,性能欠佳。
strings.Builder
修改:内部维护字节切片,可动态扩容,减少内存分配次数,拼接字符串时性能更优,尤其在处理大量字符串拼接时优势明显。
使用场景差异
- 指针修改:适用于需要直接操作原始变量内存地址,且修改操作较少的场景,或者希望通过函数修改外部变量的场景。
strings.Builder
修改:适合需要频繁进行字符串拼接的场景,能有效提升性能。
到此这篇关于Go变量指针的实现示例的文章就介绍到这了,更多相关Go变量指针内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!