C++|运算符优先级的合理性理解
C语言设计哲学要求对象的声明形式与它的使用形式尽可能相似,好处是各种不同运算符的优先级在“声明”和“使用”时是一样的,缺点在于运算符的优先级(有18级或更多,取决于你怎么算)是C语言设计不当、过于复杂之处:
1 括号运算符具有第2高的优先级
括号包括改变优先级的小括号,函数声明与调用的括号,数组声明与元素引用的中括号[],且它们都是后缀运算符(后缀运算符具有较高的优先级)。相应的,指针声明与解引用运算符*是前缀运算符,相对于后缀运算符而言,具有相对较低的优先级。
优先级问题 | 表达式 | 人们可能误以为的结果 | 实际结果 |
[]高于* | int *ap[] | int (*ap)[] | int *(ap[]) |
ap是个指向int数组的指针 | ap是元素为int指针的数组 | ||
函数()高于* | int *fp () | int (*fp)() | int *(fp ()) |
fp是个函数指针,所指函数返回int | fp是个函数,返回int* |
2 逗号运算符为什么是最低的优先级
逗号有区分为两部分的潜在含义。
逗号表达式有按顺序运算的含义,具有右结合性,其值是最后面(最右边)部分的值。
3 赋值和复合赋值的运算符为什么是倒数第二级的优先级
将右值赋给左值,自然要先计算右值,而右值的计算自然涉及到全部运算符(逗号除外)。
4 赋值运算符与逗号运算符
i = 1,2;
优先级问题 | 表达式 | 人们可能误以为的结果 | 实际结果 |
逗号运算符在所有运算符中 优先级最低 | i = 1,2; | i = (1,2); | (i=1),2 |
i 的最终结果将是什么?对,我们知道逗号运算符的值就是最右边操作数的值。但在这里,赋值符的优先级更高,所以实际情况应该是:
(i = 1), 2; /* i的值为 1 */
i赋值为1,接着执行常量2的运算,表达式整体的结果是2,计算结果丢弃。最终,i的结果是 1而不是2。
4 位移运算符和插入、提取流运算符<<、>>的优先级
位移运算符是一种特殊的算术运算。左移n位相对于乘以2的n次幂,右移n位相当于除以2的n次幂。
msb << 4 + lsb
优先级问题 | 表达式 | 人们可能误以为的结果 | 实际结果 |
算术运算高于移位运算符 | msb << 4 + lsb | (msb << 4) + lsb | msb << (4 + lsb) |
流提取流运算符<<、>>是对位移运算符的重载,重载不能改变其优先级。
std::cout << (3 & 5); //<<相对于&,具有较高的优先级
5 单目运算符具有较高的优先级
6 关系运算符的优先级通常高于算术运算运算符
关系运算符通常是一个二元 比较,先完成算术运算后才做关系运算。
优先级问题 | 表达式 | 人们可能误以为的结果 | 实际结果 |
==和!=高于位操作符 | (val & mask != 0) | (val & mask) != 0 | val & (mask != 0) |
==和!=高于赋值符 | c = getchar() != EOF | (c = getchar()) != EOF | c = (getchar() != EOF) |
7 按位运算符(不包括移位)的优先级低于关系运算符
按位运算符(不包括移位)是另一种关系,一种低层次关系。
8 逻辑运算符的优先级通常高于关系运算符
逻辑运算符是复杂关系的结合。
9 成员运算符.比解引用运算符具有更高的优先级
#include <stdio.h>struct pair_tab{ int a; int b}pair,*ppair;void test(){ ppair pp = (ppair)malloc(sizeof(pair)); pp->a = 3; pp->b = 4; printf("%d\n",(*pp).a); // 不能是*pp.a,因为struct member operator.有更高的优先级,}
为什么struct member operator.有更高的优先级?因为成员运算符是将基址与偏移地址结合成一个整体来引用一个内存块,会很自然地结合为一个整体。
-End-