Golang中指针的使用让我依然困惑
Golang中指针的使用让我依然困惑 首先我要说明,我没有任何信息学背景,但我确实喜欢编程,而且我喜欢用Go语言来编程 🙂
我并非新手,我已经用Go语言(虽然是断断续续地)编程4-5年了,鉴于我已经成功创建了多个运行良好的应用程序,我相信我对Go生态系统有很好的了解。
然而,有些东西我真的不明白,那就是指针。需要澄清的是,我理解指针是什么,但我真的无法领会应该在何处/何时/为何使用它们。
为什么有人会这样做:
i, j := 42, 2701
p := &i // 指向 i
fmt.Println(*p) // 通过指针读取 i
*p = 21 // 通过指针设置 i
fmt.Println(i) // 查看 i 的新值
p = &j // 指向 j
*p = *p / 37 // 通过指针除以 j
fmt.Println(j) // 查看 j 的新值
而不是这样做?
i, j := 42, 2701
fmt.Println(i) // 直接读取 i
i = 21 // 直接设置新的 i 值
fmt.Println(i) // 查看 i 的新值
j = j / 37 // 直接除以 j
fmt.Println(j) // 查看 j 的新值
另外,是否有人能用不那么简单的例子向我解释,在哪些情况下指针比直接调用变量更有优势?
更多关于Golang中指针的使用让我依然困惑的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我也是个新手,但在我看来,这其中的区别就像包裹和包裹地址之间的区别一样。一张纸的重量要轻得多。 🙂
我在这里找到了一个解释:
关于在何处/何时/为何使用指针,我所见过的最佳解释是在 Bill Kennedy 的这个视频课程中。

Ultimate Go: Advanced Concepts | Ardan Labs
本课程构建了层层基础知识,将使您对 Go 编程语言有更深入的理解。
价格不菲,但质量上乘。
感谢详细的解释,事情开始变得清晰了 🙂
为了确保我完全理解你的第一个案例:
- 获取/设置存储在特定内存中的一组数据,而不是到处复制粘贴(例如,接受非指针类型的函数通常会作为副本复制,并不直接修改原始数据)。
这是否意味着如果我这样做:
func Calculate(i, j int) (int, int) {
fmt.Println(i) // 读取 i,是副本吗?
i = 21 // 为 i 设置新值,是设置到它的副本里吗?
fmt.Println(i) // 查看 i 的新值,是从它的副本里吗?
j = j / 37 // 除以 j
fmt.Println(j) // 查看 j 的新值
return i, j // 这些是副本对吗?它们会被 GC 处理吗?
}
func main() {
i := 42
j := 2701
i, j = Calculate(i, j)
fmt.Printf("After caculate i is: %v\n", i) // 你不会得到 42
fmt.Printf("After caculate j is: %v\n", j) // 你不会得到 2701
}
i 和 j 在 Calculate 函数内部使用时是被复制的吗?这意味着 i = 21 和 j = j / 37 实际上是在内存中分配新的地址,而不是直接修改已经分配的那些地址?
如果是这种情况,如果 Calculate 函数如下所示会发生什么:
func Calculate(i, j int) (int, int) {
m := i / 2
n := j / 37
return m, n
}
m 和 n 肯定是分配了内存的。那么 i 和 j 呢,它们在这里被复制了吗?
在Go中指针的核心价值在于共享内存和避免复制,特别是在处理大型数据结构或需要跨作用域修改变量时。以下是几个实际场景的示例:
1. 修改函数外的变量
func increment(n *int) {
*n++
}
func main() {
x := 10
increment(&x)
fmt.Println(x) // 输出 11
}
如果不使用指针,函数内的修改不会影响原始变量。
2. 处理大型结构体
type BigData struct {
data [1000000]int
}
func process(b *BigData) {
b.data[0] = 100
// 操作结构体...
}
func main() {
var b BigData
process(&b) // 只传递指针(8字节),避免复制整个结构体
}
传递指针避免内存复制,提升性能。
3. 实现链表等数据结构
type Node struct {
value int
next *Node
}
func main() {
head := &Node{value: 1}
head.next = &Node{value: 2}
// 修改链表节点
current := head
for current != nil {
current.value *= 2
current = current.next
}
}
4. 允许nil值表示"无数据"
type Config struct {
timeout *int // nil表示未设置
}
func (c *Config) GetTimeout() int {
if c.timeout == nil {
return 30 // 默认值
}
return *c.timeout
}
5. 方法接收器指针
type Counter struct {
count int
}
func (c *Counter) Increment() {
c.count++ // 修改原始对象
}
func (c Counter) Value() int {
return c.count // 只读操作,无需指针
}
实际示例:JSON解析到现有变量
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
u := &User{}
data := []byte(`{"name":"Alice","age":30}`)
json.Unmarshal(data, u) // 必须传递指针才能修改u
fmt.Printf("%+v\n", u)
}
指针在以下情况特别必要:
- 需要修改函数参数时
- 处理大型结构体/数组时
- 实现引用语义(如链表、树)
- 区分零值和未设置值
- 方法需要修改接收器状态时
你的简单示例中确实不需要指针,但在实际项目中,指针是处理共享状态和优化内存的关键工具。



