&和是取地址与解引用操作符,非类型声明;&x获取变量地址,p访问指针所指值;传* t可修改原变量,但需确保p已初始化指向有效内存。

Go 里 & 和 * 到底在干啥
它们不是“指针类型声明”,而是两个独立的**操作符**:一个取地址,一个解引用。很多人卡在这儿,以为 * 是类型的一部分(比如 *int),其实它只是在声明变量时“告诉编译器:我要存一个地址”,真正在运行时起作用的是 & 和 * 这两个动作。
常见错误现象:cannot use &x as int value in assignment —— 把 &x 当成 x 用了;或者 invalid indirect of x (type int) —— 对非指针变量用 * 解引用。
-
&x返回的是变量x在内存中的地址,类型是*T(假设x类型为T) -
*p要求p必须是*T类型,否则编译报错;它读/写的是p所指向的那个T值 - 函数参数传
*T是为了修改原变量,但别忘了:你得先有地址才能传,也就是得用&x调用
什么时候必须用 &,什么时候不能用
核心判断只有一条:你想不想让别人能改你这个变量?想,就传 &x;不想,就传 x。Go 默认按值传递,连 struct 都拷一份——所以哪怕是个大 struct,不加 & 就进不了函数内部去改它。
使用场景举例:初始化一个结构体并让它能在函数里被填充字段、更新 map 中某个 key 对应的 struct 字段、避免大对象拷贝。
立即学习“go语言免费学习笔记(深入)”;
- 对常量、字面量不能取地址:
&42、&"hello"都非法,编译直接报cannot take the address of ... - 对 map 的 value 不能直接取地址:
&m["k"]报错,因为 map 查找返回的是拷贝,不是变量本身;得先赋值给局部变量再取址 - 对函数返回值,只有当它是“addressable”(可寻址)时才能取地址,比如返回的是局部变量或全局变量的引用;但像
&foo()(foo 返回 int)永远非法
*T 类型声明和 *v 解引用的区别
声明里的 * 是类型修饰符,运行时无行为;而表达式里的 * 是解引用操作,会真实访问内存。这两者语法相同、语义完全不同,混用就会迷糊。
典型坑:var p *int; *p = 42 —— 这会 panic:nil pointer dereference。因为 p 没指向任何有效地址。
-
var p *int只是声明一个指针变量,值为nil,不分配int存储空间 -
p = &x或p = new(int)才让它指向某处;new(T)返回的就是&T{} -
*p在未初始化前不可读写;Go 不会自动分配内存,这点比 C 更严格
指针和切片、map、channel 的关系容易被误解
切片、map、channel 本身已经是引用类型,底层结构里含指针字段,所以传它们不需要显式加 & 就能修改内容。但这不意味着它们是指针类型——reflect.TypeOf(s).Kind() 返回的是 slice,不是 ptr。
性能影响很实际:传一个 1MB 的 slice(头信息 24 字节)比传 *[1000000]int 快得多,也安全得多;后者还得手动管理内存,且无法扩容。
- 对切片做
&s是取 slice header 的地址,不是底层数组;改*&s只会影响这个 header 的副本,不影响调用方的s - map 和 channel 同理,
&m几乎没意义,除非你要交换两个 map 变量的 header(极少见) - 真正需要指针的场景,往往是“要改变变量本身的绑定目标”,比如重置一个 struct 字段为新实例,或实现链表节点
最常被忽略的一点:nil 指针和 nil 接口值不是一回事;var p *T 是 nil,但 interface{} 装了它之后,接口值不为 nil(因为底层有 type info)。这在 error 判断或反射中容易翻车。










