go指针禁止算术运算和任意内存访问,仅变量可取地址,nil解引用会panic,指针接收者避免拷贝并支持修改,new(t)仅得零值指针而&t{}支持初始化。

Go 中的指针不能做算术运算,也不能直接访问任意内存地址——这是语言设计上的硬性限制,不是技巧缺失,而是安全边界。
为什么 & 只能作用于变量,不能用于常量或表达式
因为只有变量才有确定的内存地址。常量(如 10)、字面值(如 "hello")或临时计算结果(如 a + b)不占用可寻址的存储位置。
常见错误现象:
-
&10编译报错:cannot take the address of 10 -
&(x + y)报错:cannot take the address of x + y
正确做法是先赋值给一个变量,再取地址:
x := 10 y := 20 z := x + y ptr := &z // ✅ 合法:z 是变量
这和 C 不同:Go 不允许你“制造”一个临时地址,强制你明确生命周期和归属。
*p 解引用前必须检查 p != nil
nil 指针解引用会立即触发 panic:panic: runtime error: invalid memory address or nil pointer dereference。这不是运行时警告,而是不可恢复的崩溃。
容易踩的坑:
- 函数参数声明为
*string,但调用方传了nil,而函数内部没判空就直接fmt.Println(*p) - 结构体字段是指针类型(如
Name *string),JSON 反序列化后该字段为nil,后续未检查就解引用
推荐写法(简洁且显式):
if p != nil {
fmt.Println(*p)
} else {
fmt.Println("p is nil")
}
注意:Go 不支持像 Rust 那样的非空指针类型,nil 是所有指针类型的合法零值,必须主动防御。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
结构体指针方法接收者与值接收者的内存行为差异
当你对结构体使用指针接收者(func (s *MyStruct) Do()),调用时不会复制整个结构体;而值接收者(func (s MyStruct) Do())每次调用都会拷贝一份。
影响点:
- 性能:结构体越大,值接收者拷贝开销越明显(尤其是含 slice、map 或大数组字段时)
- 修改意图:只有指针接收者能真正修改调用者的字段值;值接收者内的修改只作用于副本
- 接口实现一致性:如果某个方法用了指针接收者,那么只有
*T能满足该接口;若混用,T和*T可能无法同时实现同一接口
示例中易忽略的细节:
type User struct { Name string }
func (u *User) SetName(n string) { u.Name = n } // ✅ 修改原值
func (u User) GetName() string { return u.Name } // ⚠️ GetName 返回副本的 Name,但不影响原值
如果你看到 User{} 调用 SetName 没生效,大概率是因为误用了值接收者,或忘了传 &user。
new() 和 &T{} 的区别不只是语法糖
new(T) 返回 *T,分配零值内存并返回其地址;&T{} 构造一个零值 T 实例并取其地址。表面效果相似,但语义和适用场景不同。
关键差异:
-
new(T)只能创建零值,无法指定字段初始化;&T{Field: val}支持字段级初始化 -
new([]int)返回*[]int(指向 nil 切片的指针),不是你想要的可 append 的切片;而&[]int{1,2,3}才是有内容的切片指针 - 对于包含 unexported 字段的结构体,
new(T)是唯一能在包外获取其指针的方式(前提是导出构造函数)
实际建议:
- 优先用
&T{...}显式初始化,语义清晰,不易出错 - 仅在需要「零值指针」且不关心字段时用
new(T),比如初始化 map 的 value 类型为*sync.Mutex
别指望靠指针绕过 Go 的内存安全模型——它的“技巧”本质是约束下的精准控制,而不是越界操作。最常被忽略的,其实是把指针当“可选参数”用时,忘记 nil 是合法输入,以及把 *T 和 T 在接口实现层面混为一谈。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










