&x和x不能互换,因&x取地址得T类型值,x解引用要求x本身为T类型;二者语义相反、类型严格,错误触发编译错误或panic。

为什么 &x 和 *p 不能互换用
&x 是取地址,结果是 *T 类型的值;*p 是解引用,要求 p 本身就是 *T 类型。二者语义相反、类型约束严格,不是语法糖,而是编译器直接映射到内存寻址指令的操作。写错就会触发编译错误或 panic。
常见错误现象:
-
cannot use u (type User) as type *User:该传&u却传了u -
invalid indirect of p (type int):该写*p却对非指针变量用了* -
cannot take the address of 42:对字面量、函数调用结果(如&fmt.Sprintf("x"))用了&
什么时候必须写 &,什么时候必须写 *
关键看上下文里需要的是「地址」还是「值」。
- 函数参数声明为
*T→ 调用时必须传&x(x 是T类型变量) - 函数内部要修改入参指向的值 → 必须用
*p = ... - 结构体字段是
*string→ 初始化时得写&"hello",不能写"hello" - json.Unmarshal 接口要求
interface{},但实际期望可寻址目标 → 你得传&u,不是u
*T 在声明和使用中到底算什么
*T 有两种完全不同的语境,不看位置就容易混淆:
立即学习“go语言免费学习笔记(深入)”;
- 在变量声明、函数签名、struct 字段中:
*T是类型名,表示“指向 T 的指针”,比如var p *int - 在表达式中作用于变量:
*p是操作符,表示“读/写 p 所指的那块内存”,比如fmt.Println(*p) - 注意:
*int和*string是不同类型,不能互相赋值;nil指针解引用会 panic,用前必须检查是否非空
结构体方法接收者带 * 时,& 和 * 哪里去了
Go 编译器会自动补全,但底层逻辑没变。比如:
- 定义
func (u *User) SetName(n string) - 写
u.SetName("A")(u 是User值)→ 编译器自动转成(&u).SetName("A") - 写
pu.SetName("A")(pu 是*User)→ 直接调用,不额外加&或* - 但若方法内改了
u.Name,而你用值调用,改的是栈上副本,原变量不变——这点常被忽略
真正容易出问题的地方不在语法,而在「是否可寻址」和「是否 nil」这两个判断。传参前多看一眼变量是不是真有地址,解引用前多加一行 if p != nil,比事后 debug panic 省力得多。


















