我对C/C++语言运算规则另类小解

先看看无聊的C运算符优先级和结合性。看到表1的人可以分为三类:

  • C语言编译器的作者
  • 计算机入门者
  • 各种计算机基础能力考试应对者

比如要你计算: -Num++, +Num++, f( a(), b(), c()) ,int a= c,d,e; 之类的运算顺序。

大多数工程师为了避免记忆这繁琐的规则,使用()括号大法来解决大部分运算符优先级/结合性的问题。而且在大多数情况下,商业团队会通过统一的编程规范来避免程序语言的”坑”。

表1:运算符优先级和结合性

优先级 运算符 描述 结合性
1 ++ -- 后缀自增与自减 从左到右
() 函数调用
[] 数组下标
. 结构体与联合体成员访问
-> 结构体与联合体成员通过指针访问
(type){list} 复合字面量(C99)
2 ++ -- 前缀自增与自减[注 1] 从右到左
+ - 一元加与减
! ~ 逻辑非与逐位非
(type) 转型
* 间接(解引用)
& 取址
sizeof 取大小[注 2]
_Alignof 对齐要求(C11)
3 * / % 乘法、除法及余数 从左到右
4 + - 加法及减法
5 << >> 逐位左移及右移
6 < <= 分别为 < 与 ≤ 的关系运算符
> >= 分别为 > 与 ≥ 的关系运算符
7 == != 分别为 = 与 ≠ 关系
8 & 逐位与
9 ^ 逐位异或(排除或)
10 | 逐位或(包含或)
11 && 逻辑与
12 || 逻辑或
13 ?: 三元条件[注 3] 从右到左
14[注 4] = 简单赋值
+= -= 以和及差赋值
*= /= %= 以积、商及余数赋值
<<= >>= 以逐位左移及右移赋值
&= ^= |= 以逐位与、异或及或赋值
15 , 逗号 从左到右

去记忆这种表格明显是反直觉的,我们还是得理解编译器帮我们解释符号的意图。

C的符号可以归结为几类:

  1. 算术运算符。最普通的+ – * / % ~ & | ^ << >>
  2. 逻辑运算符。! && ||
  3. 赋值运算符。= &= <<=
  4. 比较运算符。< == >= <= > !=
  5. 成员访问。 [] () * -> .
  6. 其他 … , (type强转), sizeof, _Alignof

先是结合性,原则很简单,也很人类。

就是按照普通人的阅读顺序去记忆: 比如[], 它肯定是从左到右结合。因为 一个表达式 a[1], 先要知道a,才能知道相对寻址的 a[1]。 再比如算术运算a + b, 肯定是先move a到寄存器,在把a和b想加。因此也是从左向右结合。

那么,哪些是从右往左结合呢?

我们写代码 sizeof( SuperStruct ), 肯定是先想到SuperStruct的大小和地址在哪,然后再去取大小。这就是很直接的计算机思维

最有代表性的是: 前++,前– VS 后++, 后–

用他们来测试下这种规律的正确性:

a++ , 从左到右。因为先得知道a,才能++。

–a , 从右往左结合。 因为先得知道a,才能–。

看,这是不用记忆的。非常直接的计算机符号逻辑

优先级的记忆

优先级最高的: 除了后加/后减的优先级超高。 成员访问优先级高完全符合直觉。成员访问优先级高,可以理解为得先有数据才能做各种运算

优先级次高的: 几乎全部单目运算符优先级是次高的。单目运算符只需要一个操作数,和成员访问有相同的属性。

优先级第三/四高: 接下来是双目运算符, 乘/除/取余(优先级三) 比 加减(优先级四)优先级高,符合直觉。

优先级第五/六高: 算术运算比关系运算优先级高。 加减乘除移位比所有比较符号优先级高。移位这种算术运算 >>, << 比 >,< >=,<= 的符号优先级高。先算出来再比较。

优先级第六/七高: >=, <= 比 ==, !==优先级高。这个没有什么规则。就是C语言编译器的人自己定的规则。 先比较大小,再比较是否相等。

优先级第八/九/⑩高: 位运算自己的比较,依次是 & ^ |

优先级第十一/十二: 逻辑运算 && ||

第十三: 孤零零的一个 三目。

第十四:各种赋值

第十五:孤独的逗号。

所以,总结起来规则是:

  1. 成员运算优先级最高。成员运算符算是单目的一种,所以剩下的单目运算符次高。(单目优先原则,直觉)
  2. 算术运算符比关系比较运算符高。(先算[包括移位]出来再比较,直觉)
  3. 关系比较运算符比位运算符高。(比较完再来做位运算,非直觉)
  4. 位运算比逻辑运算符高。(有运算结果再来做逻辑与或)
  5. 逻辑运算比赋值运算符高。(做完算术,比较,位和逻辑运算再来赋值)
  6. 逗比排在最后。
  7. 结合规则完全从直觉去判断。

Lockless stack

The core logic of lockless programming is the CAS operation brought by processor. CAS can compare and exchange value between two number in a single instruction. From the perspective of CPU synchronization primitive, CAS instruction like “cmpxchg” is expensive. but from the view of application programmer, the lockfree data structure is really attractive for its free user from using more expensive operation system “mutex locks” which will switch the worker thread from background back and forth.

LockFree data-structure always comes with ABA problem, according herb sutter. for example, We need to resolve the deletion issue: we can’t delete a node if a concurrent reader/writer might be pointing to it.

there are 4 options to solve the problem:

  • Option1: Use lazy garbage collection. solve the problem because memory cannot be reused while any pointers to it exist, However, destruction of nodes becomes nondeterministic.
  • Option2: Use reference counting ( garbage collection). solve the problem in cases without cycles.
  • Option3: Never actually delete a node ( only logically delete) , can work when deleting is rare.
  • Option4: Put auxiliary nodes in between actual nodes. Contains a next pointer only, no data. These links don’t move. Enables operations on adjacent nodes to run without interference.