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

zerglurker的C语言教程009——运算符详解(一)

时间:2015-06-14 21:29:02      阅读:228      评论:0      收藏:0      [点我收藏+]

标签:c++   c语言   编程   语言   教程   

在之前几节我们讲过数据类型、讲过函数、讲过代码执行顺序以及一些添加简单函数的方法。

这一节我们将着重讲讲运算符。包括运算符的含义以及优先级的概念

在C语言中,以下运算符是被公认的:

C/C++语言运算符详解
优先级 运算符 名称以及含义 运算目 使用示例 结合方向 可否重载 附加说明
() 圆括弧 单目 (表达式) 括弧内的表达永远先计算
dynamic_cast<>() 类型动态转化 单目 dynamic_cast<目标类型>(源) C++专有,不能转换返回空
static_cast<>() 类型静态转化 单目 static_cast<目标类型>(源) C++专有,不检测转换【注1】
reinterpret_cast<>() 类型解读转化 单目 reinterpret_cast<目标类型>(源) C++专有,强制转换【注1】
const_cast<>() 去常量转化 单目 const_cast<目标类型>(源) C++专有,可将常量转非常量
0 :: 作用域解析 双目 域名::目标名 从左向右 C++专有
1 [] 下标运算符 双目 数组名[表达式] 从左向右 两种语言都有
1 () 函数调用 双目 函数名(参数) 从左向右 两种语言都有
1 . 对象成员选择 双目 对象.成员 从左向右 两种语言都有
1 -> 指针成员选择 双目 对象指针->成员 从左向右 两种语言都有
1 {} 组合运算 未知 {语句1;语句2;…语句n;} 从左向右 两种语言都有
2 ++ 自增运算 单目 变量++
++变量【注2】
从左向右
从右向左
两种语言都有
2 -- 自减运算 单目 变量--
--变量【注2】
从左向右
从右向左
两种语言都有
2 + 正号 单目 +变量 从右向左 两种语言都有
2 - 负号 单目 -变量 从右向左 两种语言都有
2 ! 逻辑非 单目 !表达式 从右向左 两种语言都有
2 ~ 按位取反 单目 ~变量 从右向左 两种语言都有
2 * 指针解引用 单目 *指针 从右向左 两种语言都有
2 & 取地址 单目 &变量 从右向左 两种语言都有
2 (类型) 强制类型转换 单目 (类型)源 从右向左 两种语言都有
2 sizeof 获取变量占用
空间大小内存
单目 sizeof(源) 从右向左 两种语言都有
2 new/new[] 分配内存 单目 new 类型名 或 new[]类型名 从右向左 C++专有
2 delete/delete[] 释放内存 单目 delete 指针 或者 delete[]指针 从右向左 C++专有
3 .* 成员对象选择 双目 对象.*成员名 或
对象.*成员指针
从左到右 C++专有
3 ->* 成员指针选择 双目 对象指针->*成员名 或
对象指针->.*成员指针
从左到右 C++专有
4 * 乘法 双目 变量1*变量2 从左到右 两种语言都有
4 / 除法 双目 变量1/变量2 从左到右 两种语言都有
4 % 取余 双目 变量1%变量2 从左到右 两种语言都有
5 + 加法 双目 变量1+变量2 从左到右 两种语言都有
5 - 减法 双目 变量1-变量2 从左到右 两种语言都有
6 << 按位左移 双目 变量1<<变量2 从左到右 两种语言都有
6 >> 按位右移 双目 变量1>>变量2 从左到右 两种语言都有
7 < 小于 双目 变量1<变量2 从左到右 两种语言都有
7 <= 不大于 双目 变量1<=变量2 从左到右 两种语言都有
7 > 大于 双目 变量1>变量2 从左到右 两种语言都有
7 >= 不小于 双目 变量1>=变量2 从左到右 两种语言都有
8 == 逻辑等 双目 变量1==变量2 从左到右 两种语言都有
8 != 不等于 双目 变量1 != 变量2 从左到右 两种语言都有
9 & 与运算 双目 变量1&变量2 从左到右 两种语言都有
10 ^ 异或运算 双目 变量1^变量2 从左到右 两种语言都有
11 | 或运算 双目 变量1|变量2 从左到右 两种语言都有
12 && 逻辑与 双目 变量1&&变量2 从左到右 两种语言都有
13 || 逻辑或 双目 变量1||变量2 从左到右 两种语言都有
14 ?: 条件运算 三目 变量1?变量2:变量3 从右到左 两种语言都有
15 = 赋值 双目 变量1=变量2 从右到左 两种语言都有
15 += 加后赋值 双目 变量1+=变量2 从右到左 两种语言都有
15 -= 减后赋值 双目 变量1-=变量2 从右到左 两种语言都有
15 *= 乘后赋值 双目 变量1*=变量2 从右到左 两种语言都有
15 /= 除后赋值 双目 变量1/=变量2 从右到左 两种语言都有
15 %= 余后赋值 双目 变量1 %= 变量2 从右到左 两种语言都有
15 <<= 左移赋值 双目 变量1 <<= 变量2 从右到左 两种语言都有
15 >>= 右移赋值 双目 变量1 >>= 变量2 从右到左 两种语言都有
15 &= 与后赋值 双目 变量1 &= 变量2 从右到左 两种语言都有
15 ^= 异或赋值 双目 变量1 ^= 变量2 从右到左 两种语言都有
15 |= 或后赋值 双目 变量1 |= 变量2 从右到左 两种语言都有
16 throw 抛异常 单目 throw 变量1 从右到左 C++专有
17 , 逗号运算 双目 变量1,变量2 从左到右 两种语言都有





























































注1: reinterpret_cast<>()和static_cast<>()貌似都是强制转换,但是两者是不同的

观察下面的代码:

class A {
public:
	A(){ m_a = 1; }
	int m_a;
};

class B {
public:
	B(){ m_b = 2; }
	int m_b;
};

class C : public A, public B 
{
};

void OperatorPriority()
{
	
	C c;
	printf("%p, %p, %p\n", &c, reinterpret_cast<B*>(&c), static_cast <B*>(&c));
	B* b1 = &c, *b2 = reinterpret_cast<B*>(&c), *b3 = static_cast <B*>(&c);
	printf("%d, %d, %d\n", b1->m_b, b2->m_b, b3->m_b);
}

执行OperatorPriority()函数后,输出结果如下:

技术分享

这是什么意思?

这说明在转换的时候reinterpret_cast<>()是无脑转换,完全不管前因后果,进行强制转换

static_cast<>()会先尝试匹配一下,能匹配则会做出处理,否则则进行强制转换

所以m_b static_cast<>()会输出正确值,而reinterpret_cast<>()会给出一个错误值

但是为什么强制类型转换又可以正确输出m_b呢?这就涉及到c++的多态特性,以后我会讲到的。这里只讨论运算符,不做深入讲解。

注2:前缀自增/减运算 和 后缀自增/减运算

前缀自增/减 是这样的 ++变量 --变量

后缀自增/减 是这样的 变量++ 变量--

有很多资料说后缀自增/减运算 优先级高于前缀

这个说得太简单,实际情况非常复杂(也就是说这种说法完全是拍脑袋,没有实践的扯淡!)

首先,来看代码:

从上面的代码可以很明显的看出,如果认为后缀自增运算有所谓的高优先级,那么完全就说不通

首先++a--;语句根本就是个语法错误!前缀和后缀运算符不能同时进行,所以也无法比较他们的优先级。

不过这不能说明网上的优先级说明是错误的,因为我们可以拿它们同级的其他运算符进行比较

注意test002函数 !运算是和前缀自增运算同级的运算符,按理说应该是先计算后缀自增然后再计算非,

这样就应该等价于a=!(a++) 即结果应该为0 。但是为什么输出的却是1呢?

答案很复杂。上面真实的运算是这样进行:

先计算计算a++ 即0++=1 保存结果到a 然后按照a=0计算!a即 !0=1,再次保存结果到a 导致的结果就是a最后变成了1

下面是该代码的反汇编

	a = !a++;
00113EBD 8B 45 F8             mov         eax,dword ptr [a]  
00113EC0 89 85 30 FF FF FF    mov         dword ptr [ebp-0D0h],eax  
00113EC6 8B 4D F8             mov         ecx,dword ptr [a]  
00113EC9 83 C1 01             add         ecx,1  
00113ECC 89 4D F8             mov         dword ptr [a],ecx  
00113ECF 83 BD 30 FF FF FF 00 cmp         dword ptr [ebp-0D0h],0  
00113ED6 75 0C                jne         main+0B4h (0113EE4h)  
00113ED8 C7 85 2C FF FF FF 01 00 00 00 mov         dword ptr [ebp-0D4h],1  
00113EE2 EB 0A                jmp         main+0BEh (0113EEEh)  
00113EE4 C7 85 2C FF FF FF 00 00 00 00 mov         dword ptr [ebp-0D4h],0  
00113EEE 8B 95 2C FF FF FF    mov         edx,dword ptr [ebp-0D4h]  
00113EF4 89 55 F8             mov         dword ptr [a],edx
从表面上看,后缀++好像是优先运算的。但是我们不能只关注这点,而是要关注它对结果的影响
运算并不是先计算的就是高优先级的——如果该运算更不不能影响结果
这个表达式的运算a++的结果是被舍弃了,也就是!运算符屏蔽了后缀++!!!

是不是感觉逻辑有点乱?没有关系,我们再看其他函数,这样你就会更加凌乱!

看函数test003 test004

003的计算是这样进行的:先计算a+a 即 0+0 得到0,再计算自增 即0++ 得到1 。最后进行赋值,取a++的值1

下面是实际的计算过程

00DD3E55 8B 45 F8             mov         eax,dword ptr [a]  
00DD3E58 03 45 F8             add         eax,dword ptr [a]  
00DD3E5B 89 45 F8             mov         dword ptr [a],eax  
00DD3E5E 8B 4D F8             mov         ecx,dword ptr [a]  
00DD3E61 83 C1 01             add         ecx,1  
00DD3E64 89 4D F8             mov         dword ptr [a],ecx 
也就是说,这里先计算了a+a,然后才计算的a++

这不是说明+运算符的优先级高于后缀++吗!!

004的计算是这样进行的:先计算2+a 即2+0 得到2,再计算自增,即2++,得到3

01383E89 8B 45 F8             mov         eax,dword ptr [a]  
01383E8C 83 C0 02             add         eax,2  
01383E8F 89 45 F8             mov         dword ptr [a],eax  
01383E92 8B 4D F8             mov         ecx,dword ptr [a]  
01383E95 83 C1 01             add         ecx,1  
01383E98 89 4D F8             mov         dword ptr [a],ecx 
得到同样的逻辑结果 +运算符优先级高于后缀++

现在我们来总结一下:

后缀运算符 和!运算符在一起的时候,后缀运算符会被屏蔽

后缀运算符和+-*/等运算符在一起的时候,先计算其他,再计算自增

现在你是不是已经彻底糊涂了?

很好,这样就对了。

因为我们现在得到一个结论:

1 那些所谓的优先级,全部是扯淡,千万不要指望优先级能够完全按照你的预想来执行

2 请用圆括弧安排好优先级——只有这个是唯一可靠的伙伴!

下一节开始我将逐个对运算符进行分析和讲解,敬请期待……
















zerglurker的C语言教程009——运算符详解(一)

标签:c++   c语言   编程   语言   教程   

原文地址:http://blog.csdn.net/zerglurker/article/details/46489133

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