
Go 不允许直接对函数调用结果取地址(如 &foo()),因为函数返回值不是可寻址的;若需指针,应修改函数签名使其直接返回指针类型,避免不必要的值拷贝。
go 不允许直接对函数调用结果取地址(如 `&foo()`),因为函数返回值不是可寻址的;若需指针,应修改函数签名使其直接返回指针类型,避免不必要的值拷贝。
在 Go 中,& 操作符要求其操作数必须是可寻址的(addressable)——即必须是变量、结构体字段、切片或数组元素、解引用后的指针,或复合字面量。而函数调用(如 foo())的返回值属于临时值(r-value),不具备内存地址,因此编译器会报错:
func foo() (int64, error) { return 42, nil }
var A *int64
A, err = &foo() // ❌ 编译错误:cannot take the address of foo()? 错误信息示例:cannot take the address of foo() 或 cannot take address of foo() call
✅ 正确做法:让函数直接返回指针
若返回值较大(如大型结构体),且你确实需要其指针以避免拷贝,最符合 Go 惯例且高效的方式是重构函数签名,使其第一返回值为指针类型:
func foo() (*int64, error) {
val := int64(42)
return &val, nil // ✅ 合法:&val 作用于局部变量(可寻址)
}
// 使用方式
p, err := foo()
if err != nil {
log.Fatal(err)
}
fmt.Println(*p) // 输出:42⚠️ 注意:上述 &val 是安全的,因为 val 是函数内定义的局部变量,其生命周期由 Go 的逃逸分析保证——当它被返回为指针时,编译器会自动将其分配到堆上。
❌ 常见误区与替代方案辨析
不能用临时变量“绕过”限制?
即使写成 tmp := foo(); A = &tmp,也无效:foo() 返回两个值,无法一次性赋给单个变量;而若只取第一个值 tmp, _ := foo(),则 tmp 是副本,&tmp 虽合法,但并未避免拷贝——你仍需先复制整个 int64(虽小,但对大结构体无意义)。不推荐:通过全局/包级变量中转
违反纯函数原则,破坏并发安全性,且无法解决根本问题。不推荐:使用 unsafe 或反射
不仅违背语言设计哲学,更易引发未定义行为,且无法规避拷贝(返回值仍需构造)。
✅ 最佳实践总结
| 场景 | 推荐方案 |
|---|---|
| 需要避免大对象拷贝,且调用方需指针 | 修改函数返回 *T, error,内部用 &localVar 返回 |
| 函数已固定签名,无法修改 | 接受一次拷贝(Go 对小类型如 int64 拷贝开销极低;对大结构体,应优先审视是否真需指针接口) |
| 需统一处理多种返回类型 | 封装为结构体并返回其指针:func foo() (*Result, error) |
简言之:Go 的设计鼓励清晰、显式的内存语义。与其尝试绕过语言规则,不如顺应它——让接口契约明确表达“我返回一个可共享的指针”,这既是性能优化,也是代码可维护性的体现。

















