You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言结构体指针访问成员时&取地址符的使用疑问

解答

你的误解本质来自两个没理清的基础点:C语言运算符优先级规则,以及C语言值传递下「修改函数外变量必须传地址」的通用要求,和scanf没有特殊绑定关系。

先理清运算符优先级:你之前对&作用对象的理解完全错了

C语言中结构体指针取成员运算符->的优先级,远高于取地址运算符&。所以&personPtr->age的运算顺序是:

  1. 先算personPtr->age:顺着personPtr指针找到对应的结构体实例,定位到其中的age成员,得到age这个变量本身
  2. 再对这个age成员做&取地址操作,最终得到的是结构体内部age成员的内存地址

这个表达式根本不是“取personPtr指针本身的地址再找age”——你觉得“取指针的地址来写成员是错的”这个判断本身没问题,只是你搞错了运算顺序,&从头到尾都没作用在personPtr上。

对应你举的struct A的例子:

  • f2->x:拿到f1结构体中x成员存储的具体值,类型是int
  • &f2->x:等价于&(f2->x),拿到f1结构体中x成员所在的内存地址,类型是int*,和直接写&f1.x得到的结果完全一致。

为什么scanf必须传&personPtr->age,不能直接传personPtr->age

C语言所有函数传参默认是值传递:函数拿到的是传入参数的一份临时拷贝,和原变量没有关联。
scanf的核心作用是把你输入的内容写入到对应的变量中,它必须拿到目标变量的内存地址,才能顺着地址把值写到对应的内存空间里:

  • 如果传personPtr->age:你传给scanf的是age当前存的整数值(比如未初始化的垃圾值),scanf会把这个随机数当成内存地址去写入内容,直接触发内存访问错误,就算不崩溃也写不到真正的age变量里。
  • 如果传&personPtr->age:你传给scanf的是age变量的真实内存地址,scanf才能正确把输入值写到age的内存位置。

而printf不需要加&的原因也很简单:printf只需要读取变量的值用来输出,不需要修改变量本身,直接传值就够了。你如果给printf传&personPtr->weight,等于把内存地址当成浮点数解析打印,当然会输出乱码。

为什么Mutex初始化代码里也要加&

这和scanf的逻辑完全一致,是C语言传参的通用规则,不是某几个函数的特殊要求:
你贴的spinlock_create、uthread_initqueue都是初始化函数,核心逻辑是修改传入的自旋锁、队列结构体的内容(比如给自旋锁设置初始未持有状态,给队列初始化头尾指针),必须拿到目标结构体的地址才能修改原变量:

  • 如果传mutex->spinlock:相当于把spinlock结构体的整个内容做了一份值拷贝传给函数,函数里修改的是这份临时拷贝,根本改不到mutex里真正的spinlock成员,初始化完全失效。
  • 传&mutex->spinlock:才是把mutex中spinlock成员的真实内存地址传进去,函数才能顺着地址修改到真实的成员变量,完成初始化。

易混淆写法对照

以你最开始的person代码为例,几个写法的含义完全不同,不要搞混:

  • personPtr:结构体指针,存储的是person1整个结构体的首地址,类型是struct person*
  • personPtr->age:person1中age成员存储的具体整数值,类型是int
  • &personPtr->age:person1中age成员的内存地址,类型是int*,等价于&person1.age
  • &personPtr:personPtr这个指针变量自身的内存地址,类型是struct person**,在你贴的所有场景里用这个写法都是逻辑错误的。

内容的提问来源于stack exchange,提问作者hodondo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 10:36:41