&是取地址操作符,是解引用操作符,二者独立且仅在特定上下文中互为逆运算;&只能作用于可寻址变量,在声明中修饰类型、在表达式中解引用。

& 是取地址,* 是解引用——它们不是“一对符号”,而是两个独立操作,只是恰好互为逆运算。理解错这一点,就容易在声明、传参、解引用时写出 panic 或逻辑错误。
什么时候必须用 &?不是“想用就用”,而是编译器强制要求
Go 是值传递语言,函数收到的是参数副本。若函数签名明确要求指针类型(比如 func modify(x *int)),你传 modify(&n) 是语法刚需;传 modify(n) 会直接报错:cannot use n (type int) as type *int in argument to modify。
- 常见场景:调用标准库方法(如
json.Unmarshal、fmt.Scanf)时,目标变量必须传地址,否则无法写入数据 - 结构体字面量初始化后立即取址是惯用写法:
p := &Person{Name: "Alice"},等价于先声明再取址,但更简洁安全 - 不能对临时值或表达式取址:
&(a + b)、&"hello"都非法——&只能作用于「可寻址的变量」(即有固定内存位置的命名变量)
* 的两种身份:声明时是类型修饰符,使用时是解引用操作符
这是初学者最易混淆的点:* 在变量声明里(如 var p *int)不执行任何运行时操作,它只是告诉编译器“这个变量存的是 int 的地址”;而 *p 出现在表达式中时,才是真正的解引用动作。
- 声明指针变量:
var ptr *string—— 此时*属于类型系统,和int、[]byte地位相同 - 解引用指针:
name := *ptr—— 此时*是运行时操作,若ptr == nil,程序 panic:invalid memory address or nil pointer dereference - 修改原值:
*ptr = "Bob"直接改的是ptr指向的内存单元,不是复制一份再赋值
结构体方法接收者用指针 vs 值,& 和 * 怎么自动参与?
Go 会隐式处理取址与解引用,但前提是类型匹配。比如你定义了 func (p *Person) SetName(n string),那么:
立即学习“go语言免费学习笔记(深入)”;
- 调用
person.SetName("Tom")时,如果person是Person类型(值),Go 自动转成(&person).SetName("Tom") - 如果
person已经是*Person类型(指针),则直接调用,不额外取址 - 但反过来不行:值接收者方法(
func (p Person) Save())不能通过指针调用并期望修改原值——因为(*p).Save()仍是对副本操作
package main
import "fmt"
type Counter struct{ val int }
func (c *Counter) Inc() { c.val++ } // 修改原结构体字段
func (c Counter) CopyInc() { c.val++ } // 只改副本,不影响原值
func main() {
c := Counter{val: 0}
c.Inc() // Go 自动 &c → (*Counter).Inc()
fmt.Println(c.val) // 输出 1
c2 := Counter{val: 0}
c2.CopyInc() // 值接收者,无自动取址,也不需要
fmt.Println(c2.val) // 输出 0,未变
}
真正容易被忽略的,是 nil 指针调用方法时的行为:只要方法不显式解引用(即不写 *p 或访问字段),哪怕接收者是指针类型,nil 调用也不会 panic。但一旦触及字段或解引用,立刻崩溃——这比 C/C++ 更隐蔽,也更依赖开发者对指针生命周期的把控。










