C语言结构体指针访问成员时&取地址符的使用疑问
解答
你的误解本质来自两个没理清的基础点:C语言运算符优先级规则,以及C语言值传递下「修改函数外变量必须传地址」的通用要求,和scanf没有特殊绑定关系。
先理清运算符优先级:你之前对&作用对象的理解完全错了
C语言中结构体指针取成员运算符->的优先级,远高于取地址运算符&。所以&personPtr->age的运算顺序是:
- 先算
personPtr->age:顺着personPtr指针找到对应的结构体实例,定位到其中的age成员,得到age这个变量本身 - 再对这个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
相关产品推荐
相关产品推荐

