You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go中返回指针的两种实现是否等价?探讨其差异与惯用写法

Go投票计数器实现的差异与正确写法

问题背景

有个编程练习要求写一个函数,接收票数参数,返回一个初始化该票数的指针,示例用法如下:

var initialVotes int
initialVotes = 2

var counter *int
counter = NewVoteCounter(initialVotes)
*counter == initialVotes // 结果为true

目前有两种能通过测试的实现:

方案A

func NewVoteCounter(initialVotes int) *int {
    counter := initialVotes
    return &counter
}

方案B(注:原代码存在笔误,将参数名initialVotes写成了initialValue,以下修正后展示)

func NewVoteCounter(initialVotes int) *int {
    return &initialVotes
}

我想确认:这两种实现并不等价,对吗?我的理解是方案A会新开辟内存、复制传入值后返回指针;方案B直接返回传入参数的指针。这个理解是否正确?有没有其他需要注意的细节?另外Go里有没有更惯用的写法?

补充:练习的测试只检查指针指向的值,不验证指针本身,所以两种方案都能通过测试,测试代码如下:

func TestNewVoteCounter(t *testing.T) {
    tests := []struct {
        name  string
        votes int
    }{
        {
            name:  "Simple vote counter with 2 votes",
            votes: 2,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := NewVoteCounter(tt.votes)
            if got == nil {
                t.Errorf("NewVoteCounter(%d) = %s, &%d", tt.votes, intPtrRepresentation(got), tt.votes)
            }
            if got != nil && *got != tt.votes {
                t.Errorf("NewVoteCounter(%d) = %s, &%d", tt.votes, intPtrRepresentation(got), tt.votes)
            }
        })
    }
}

func intPtrRepresentation(p *int) string {
    if p == nil {
        return "nil"
    }
    return "&" + strconv.Itoa(*p)
}

解答

你的理解方向是对的,但方案B存在致命的未定义行为,具体细节和正确写法如下:

1. 两种方案的核心差异

  • 方案A:函数内部声明局部变量counter,复制参数的值后返回它的指针。Go会自动将这个局部变量做栈逃逸处理(因为返回了它的指针),变量会被分配到堆上,后续访问指针是完全安全的,指向的是独立的内存空间。
  • 方案B:直接返回函数参数的指针。Go的函数参数是值传递,initialVotes是函数栈帧里的局部变量,函数执行完毕后栈帧会被销毁,返回的指针指向的是已经失效的内存区域。后续解引用这个指针属于未定义行为——可能暂时读到正确值,也可能出现垃圾数据,甚至导致程序崩溃。测试能通过只是巧合,这种写法绝对不能用。

2. Go中的惯用写法

实现这个需求最安全且简洁的写法有两种:

写法一:局部变量复制(方案A的简化版)

func NewVoteCounter(initialVotes int) *int {
    v := initialVotes
    return &v
}

这种写法清晰直观,是日常开发中最常用的方式之一。

写法二:使用new函数分配内存

func NewVoteCounter(initialVotes int) *int {
    p := new(int)
    *p = initialVotes
    return p
}

new(int)会直接在堆上分配一个int类型的内存空间,返回其指针,之后我们再给它赋值即可,逻辑同样清晰。


内容的提问来源于stack exchange,提问作者Greg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 19:53:14