码迷,mamicode.com
首页 > 编程语言 > 详细

【C/C++】运算符及其优先级

时间:2016-05-02 20:00:45      阅读:477      评论:0      收藏:0      [点我收藏+]

标签:

    首先介绍表达式及语句的基本概念。

表达式:

    一个表达式由运算量(亦称操作数)和运算符组成。例如,算术表达式A+B是由二元运算符"+"和操作数A、B组成的。A、B分别称为运算符+的左、右操作数。

    在表达式中,一元运算符通常写在它的操作数前面,如-A,这种形式称为前缀形,但也有也在操作数的后面,如A++,这种形式称为后缀形。二元运算符一般写在两个操作数的中间,如X+Y。

    表达式的性质在后文中会有所提及,如自增、自减运算符只能作用于变量,而不能作用于常量或表达式。

语句:

    语句通常是以分号结尾的,从功能上说,语句大体可分为执行性语句和说明性语句两大类。说明性语句旨在定义各种不同数据类型的变量或运算。执行性语句旨在描述程序的动作。执行性语句又可分为赋值语句、控制语句和输入/输出语句。

    语句和表达式的区别是:表达式是一个值,而语句没有值。

1. 赋值语句

    在赋值语句中,赋值运算符“=”左、右两边的变量名扮演着两种不同的角色。对赋值运算符右边的B我们需要的是它的值;对左边的A我们需要的是它所代表的存储单元(的地址)。为了区分一个名字的这两种特征,我们把一个名字所代表的单元(地址)称为该名的左值;把一个名字的值称为该名的右值。

    赋值操作符的左操作数必须是非const的左值。

2. 自增与自减运算符

    ++和--,分别称为自增运算符和自减运算符,它们是一元算术运算符。学习和应用这两个运算符时应注意前缀运算和后缀运算的区别。

2.1 前缀运算与后缀运算

    仍以自增运算符为例,该运算符可作用在变量之前,例如++i,称为前缀运算;也可作用于变量之后,例如i++,称为后缀运算。

    以++操作为例,对于变量a,++a表示取a的地址,增加它的内容,然后把值放在寄存器中;a++表示取a的地址,把它的值装入寄存器,然后增加内存中a的值。

    自减运算符与自增运算符类似,只要将加1改为减1即可。即前缀运算是“先变后用”,而后缀运算是“先用后变”。 

2.2 自增、自减运算符作用的对象

    自增、自减运算符只能作用于变量,而不能作用于常量或表达式。只要是标准类型的变量,不管是整型、实型,还是字符型、枚举型都可以作为这两个运算符的运算对象。

    如以下四个表达式都是合法的:

    1) i++ + j++; 

    2) ++i + (++j); 

    3) ++a + b++; 

    4) ++array[--j]; 

    而++6、(i+j)++、‘A’++、++i+++j、(&p)++这五个表达式都是不合法的。

    为什么i+++j++合法,而++i+++j却不合法呢?这是因为C/C++的编译器对程序编译时,从左到右尽可能多地将字符组合成一个运算符或标识符,因此i+++j++等效于(i++)+(j++),两个"++"作用的对象都是变量,这是合法的;而++i+++j等效于++(i++)+j,第一个"++"作用的是表达式"i++",这是不允许的。

【示例】下列代码是否有错误,若有错误请指出,若没有,请确定a的值是多少?

1 int main()
2 {
3     int a = 3; 
4     a += (a++); 
5     a += (++a); 
6     (++a) += (a++); 
7     (a++) += a; 
8     return 0; 
9 }

【参考答案】

技术分享
 1 int main()
 2 {
 3     int a = 3;         // 此句执行后,a = 3
 4     a += (a++);     // 此句执行后,a = 7 
 5     a += (++a);     // 此句执行后,a = 16 
 6     (++a) += (a++); // 此句执行后,a = 35 
 7     (a++) += a;      // 编译错误,a++是右值,只能位于等号的右边
 8                     // 左值既可以位于等号的左边,也可以位于等号的右边 
 9     return 0; 
10 }
View Code

2.3 ++、--运算符的结合方向

    表达式k=-i++等效于k=(-i)++还是k=-(i++)?因为负号运算符和自增运算符优先级相同,哪一个正确就得看结合方向。自增、自减运算符及负号的结合方向是从右向左的。因此,上式等效于k=-(i++)。

    注意:不用因为k=-i++等效于k=-(i++)就先做"++"运算!这里采用的是"先用后变",即先拿出i的值做负号"-"运算,把这个值赋给变量k之后变量i才自增。

3. 关系与逻辑运算符

    关系操作符(<、<=、>、>=)具有左结合性。事实上,由于关系运算操作符返回bool类型的结果,因此很少使用其左结合特性。如果把多个关系操作符串接起来使用,结果往往出乎意料:

if (i < j < k)  { /*...*/ }

    在这种写法中,只要k大于1,上述表达式的值就为true。这是因为第二个小于操作符的左操作数是第一个小于操作符的结果:true或false。也就是,该条件将k与整数0或1做比较。为了实现我们想要的条件检验,应重写上述表达式如下:

if (i < j && j < k) { /*...*/ }

    逻辑操作符将其操作数视为条件表达式:首先对操作数求值;若结果为0,则条件为假(false),否则为真(true)。仅当逻辑与(&&)操作符的两个操作数都为true,其结果才为true。对于逻辑或(||)操作,只要它的两个操作数之一为true,它的值为true。

    给定以下两种形式:

expr1 && expr2  // logical AND
expr1 || expr2  // logical OR

    仅当由expr1不能确定表达式的值时,才会解expr2。

    逻辑与和逻辑或操作符总是先计算其左操作数,然后再计算右操作数。只有在仅靠左操作数的值无法确定该逻辑表达式的结果时,才会求解右操作数。我们常常称这种求值策略为"短路求值"。

4. 位运算符

    位操作符使用整型的操作数。位操作符将其整型操作数视为二进制位的集合,为每一位提供检验和设置功能。C语言提供了以下六种位运算符。

操作符 功能 单双目 用法
& 按位与 双目运算符 expr1 & expr2
按位或 双目运算符 expr1 | expr2 
^ 按位异或 双目运算符 expr1 ^ expr2
~ 取反 单目运算符 ~expr
<< 左移 双目运算符 expr1 << expr2
>> 右移 双目运算符 expr1 >> expr2

【示例】不用算术运算符实现两个数的加法。

【解析】对于二进制加法运算,若不考虑进位,则1+1=0,1+0=1,0+1=1,0+0=0,通过对比异或,不难发现,此方法与异或类似。因而排除进位,加法可以用异或来实现。

    然后考虑进位,0+0的进位为0,1+0的进位为0,0+1的进位为0,只有1+1时进位才为1,该操作与位运算的&操作相似。

    那么加法运算可以这样实现:

    1) 先不考虑进位,按位计算各位累加(用异或实现),得值a;

    2) 然后计算进位,并将进位的值左移,得值b,若b为0,则a就是加法运算的结果,若b不为0,则a+b即得结果(递归调用该函数)。

参考代码如下:

技术分享
1 int addWithoutArithm(int a, int b)
2 {
3     if (b == 0) return a; // 当没有进位时 
4     int sum = a^b; 
5     int carry = (a & b) << 1; 
6     return addWithoutArithm(sum, carry); 
7 } 
View Code

【示例】如果X大于0并小于65536,用移位法计算X乘以255的值为( )。(2011网易游戏)

【解答】:-X + (X << 8)。

5. 运算符优先级表

    表达式的运算顺序主要由以下两种因素决定:

    1) 运算符的优先级:程序总是先执行优先级较高的运算符;

    2) 运算符的结合性:当运算符的优先级相同时,运算符的结合性决定运算顺序。对从左到右的运算符,先执行左边的部分,对从右向左的运算符,则先执行右侧的部分。

    C语言的运算符优先级表如下所示:

优先级 运算符 名称或含义 使用形式 结合方向 说明
1 [] 数组下标 数组名[常量表达式] 左到右  
1 () 圆括号 (表达式)/函数名(形参表) 左到右  
1 . 成员选择(对象) 对象.成员名 左到右  
1 -> 成员选择(指针) 对象指针->成员员 左到右  
2 - 负号运算符 -表达式 右到左 单目运算符
2 (类型) 强制类型转换 (数据类型)表达式 右到左  
2 ++ 自增运算符 ++变量名/变量名++ 右到左 单目运算符
2 -- 自减运算符 --变量名/变量名-- 右到左 单目运算符
2 * 取值运算符 *指针变量 右到左 单目运算符
2 & 取地址运算符 &变量名 右到左 单目运算符
2 ! 逻辑非运算符 !表达式 右到左 单目运算符
2 ~ 按位取反运算符 ~表达式 右到左 单目运算符
2 sizeof 长度运算符 sizeof(表达式) 右到左  
3 / 表达式/表达式 左到右 双目运算符
3 * 表达式*表达式 左到右 双目运算符
3 % 余数(取模) 整型表达式%整型表达式 左到右 双目运算符
4 + 表达式+表达式 左到右 双目运算符
4 - 表达式-表达式 左到右 双目运算符
5 << 左移 变量<<表达式 左到右 双目运算符
5 >> 右移 变量>>表达式 左到右 双目运算符
6 > 大于 表达式>表达式 左到右 双目运算符
6 >= 大于等于 表达式>=表达式 左到右 双目运算符
6 < 小于 表达式<表达式 左到右 双目运算符
6 <= 小于等于 表达式<=表达式 左到右 双目运算符
7 == 等于 表达式==表达式 左到右 双目运算符
7 != 不等 表达式!=表达式 左到右 双目运算符
8 & 按位与 表达式&表达式 左到右 双目运算符
9 ^ 按位异或 表达式^表达式 左到右 双目运算符
10 按位或 表达式|表达式 左到右 双目运算符
11 && 逻辑与 表达式&&表达式 左到右 双目运算符
12 || 逻辑或 表达式||表达式 左到或 双目运算符
13 ?: 条件运算符 表达式1?表达式2:表达式3 右到左 三目运算符
14 = 赋值运算符 变量=表达式 右到左  
14 /= 除后赋值 变量/=表达式 右到左  
14 *= 乘后赋值 变量*=表达式 右到左  
14 %= 取模后赋值 变量%=表达式 右到左  
14 += 加后赋值 变量+=表达式 右到左  
14 -= 减后赋值 变量-=表达式 右到左  
14 <<= 左移后赋值 变量<<=表达式 右到左  
14 >>= 右移后赋值 变量>>=表达式 右到左  
14 &= 按位与后赋值 变量&=表达式 右到左  
14 ^= 按位异或后赋值 变量^=表达式 右到左  
14 |= 按位或后赋值 变量|=表达式 右到左  
15 , 逗号运算符 表达式,表达式,... 左到右 从左向右顺序计算

    运算符优先级有几个简单的规则:

    1) 括号,下标,->和.(成员)最高;

    2) 单目的比双目的高;算术双目比其他双目的高;

    3) 移动运算高于关系运算;关系运算高于按位运算(与,或,异或);按位运算高于逻辑运算;

    4) 三目的只有一个条件运算,低于逻辑运算;

    5) 赋值运算仅比","高,且所有的赋值运算符优先级相同,结合访问从右向左。

【C/C++】运算符及其优先级

标签:

原文地址:http://www.cnblogs.com/xiaoxxmu/p/5452886.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!