接着上面的例子,我们已经分析了t_ptr的内存布局,它的值是一个地址 。问题就来了,你想过没有,如果一个符号,它的值保存了一个地址,我对他能做什么操作?我们知道,如果t_ptr的值是int、long,我就能用CPU的算术模块对它们进行“加减乘除”,这样是有意义的,因为我在做代数运算 。那么对一个地址,显然,做加减乘除运算是没有意义的 。我们唯一能对地址做的有意义的操作就是找到这块地址,对这个地址对应的内存进行操作,这才是地址类型数据的意义 。
因为对地址进行普通意义上的四则运算是没有代数意义的,所以,C语言为地址数据类型(指针)增加了两个操作符*与-> 。
- *就是切换符号的含义,如*ptr = 3那么=获取的内存值,并不是ptr这个符号本身的值,而是ptr的值所对应的内存地址的内存值 。相当于将符号ptr的含义进行了切换,切换到了新的目标内存地址;
- ->也是切换符号含义,但是不是切换到ptr指向的大内存块,而是里面的小内存块,可以理解成切换成对成员变量的符号的内存访问 。
看个Linux内核中的例子,这是mcs spinlock的加锁操作
static inlinevoid mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)struct mcs_spinlock *prev;/* Init node */node->locked = 0;node->next = NULL;prev = xchg(lock, node); //相当于把mcslock.next = node;同时返回*lock修改之前的值 。if (likely(prev == NULL)) { //原来*lock指向NULL 。也就是现在链表还没形成,没有竞争 。return;} // 如果有值说明有竞争,要排队 。所以直接插入最后就行了 。prev就是最后一个元素 。WRITE_ONCE(prev->next, node);/*这里是个spin loop 。在percpu自身的lock上面自旋,等待变成1,获取锁 。/* Wait until the lock holder passes the lock down. */arch_mcs_spin_lock_contended(&node->locked);
- struct mcs_spinlock **lock是什么呢?书上会说指向指针的指针,这么说没错,但是对很多刚刚接触到C语言的人来说其实很难理解,很难对应到实际内存中的样子 。不如,一步步拆解这条语句的含义 。
-
- 首先,我们要先找到符号,符号才是内存,才有意义 。显然这里描述的符号是lock;
- 符号就是一块内存,是内存就有地址与值,那么lock的值的信息可以从它的类型来推断出来,struct mcs_spinlock **就是类型信息,它的主要作用是描述lock的值有多长 。那么有多长呢?看到*就不用看struct mcs_spinlock了,就是一个地址,另一个符号的地址,8个字节长,保存在了lock符号的值里面 。
- struct mcs_spinlock **的含义是什么呢?这是一个递归定义 。根据前面对*运算符的解释,struct mcs_spinlock **可以展开成这种形式(struct mcs_spinlock *)(*lock) 。*lock的含义是切换符号,假设切换成了(struct mcs_spinlock *)_lock符号,_lock符号的地址是lock的值,而_lock的值又是一个地址,一个struct mcs_spinlock *类型的地址 。如下图:
文章插图
指针的指针到底是什么?
推荐阅读
- 潮汕美食:探索舌尖上的美味与文化
- 生姜薏米水功效与作用
- 生姜紫苏水功效与作用
- 白酒泡樱桃功效与作用
- 于和伟|于和伟乱睡门后露面,与风波女主眼神躲闪刻意避嫌王丽坤再受累
- |解读电视剧《爱情而已》探寻当代人情感、职场与家庭之路
- 面膜泥的功效与作用,面膜泥的正确使用方法 面膜泥多久后洗掉?
- 附桂骨痛颗粒的功效与作用
- 百合莲子绿豆汤的功效与作用
- 红糖与赤砂糖有什么区别