
本文详解go函数返回指针时为何`&i`打印结果不一致——关键在于混淆了“指针变量自身的地址”与“指针所指向的地址”,通过对比c语言行为,厘清go内存模型中的指针语义。
在Go中,当你从函数返回一个指针(如 *int),实际返回的是指向堆(或逃逸分析后分配位置)上某个整数的内存地址值。但初学者常犯的一个典型错误,是误将“接收该指针的变量的地址”(即 &i)当作“指针所指向的目标地址”来打印和比较。
回顾你的代码:
func createPointerToInt() *int {
i := new(int) // 分配一个int,返回指向它的指针(例如:0x1040a128)
fmt.Println(&i) // ❌ 打印的是局部变量 i(指针类型)自身的地址,不是它指向的地址!
return i
}
func main() {
i := createPointerToInt() // i 是 *int 类型,其值为 0x1040a128
fmt.Println(&i) // ❌ 再次打印 main 中变量 i 自身的地址(如 0x1040a120)→ 两个栈变量地址不同,差8字节属正常(64位系统下指针占8字节,相邻栈变量偏移合理)
}⚠️ 关键点解析:
- i 在 createPointerToInt 中是一个局部指针变量,new(int) 返回的地址被赋给它;&i 取的是这个局部变量在栈上的地址(函数返回后该变量已销毁);
- main 中的 i 是另一个独立的指针变量,它接收了返回的地址值(如 0x1040a128),而 &i 是这个新变量在 main 栈帧中的地址;
- 因此 &i 在两处打印的是两个不同栈变量的地址,自然不同——这与C中 printf("%#08x\n", r) 正确打印指针值(而非 &r)形成鲜明对比。
✅ 正确做法:要验证指针是否成功传递目标地址,应直接打印指针变量本身(即地址值),而非其地址:
立即学习“go语言免费学习笔记(深入)”;
func createPointerToInt() *int {
i := new(int)
*i = 42
fmt.Printf("Inside: pointer value = %p, pointed value = %d\n", i, *i)
return i
}
func main() {
p := createPointerToInt()
fmt.Printf("Outside: pointer value = %p, pointed value = %d\n", p, *p)
// 输出示例:
// Inside: pointer value = 0xc000014090, pointed value = 42
// Outside: pointer value = 0xc000014090, pointed value = 42 ← 地址值一致!
}? 补充说明:
- Go中 new(T) 总是在堆上分配零值 T 并返回 *T(除非被编译器优化到栈上,但语义保证安全);
- 指针值(如 0xc000014090)是可复制、可返回、可跨作用域传递的“地址数据”,它不依赖于原函数栈帧;
- 若需调试内存布局,优先使用 %p 格式化动词打印指针值,避免 & 引发的语义混淆。
总结:Go指针的“值”就是内存地址,函数返回指针的本质是返回一个地址数值;而 &variable 永远表示“该变量在当前作用域中的存储位置”,二者层级不同,切勿混用。










