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

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

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

在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(类型)强制类型转换单目(类型)源从右向左两种语言都有
2sizeof获取变量占用
空间大小内存
单目sizeof(源)从右向左两种语言都有
2new/new[]分配内存单目new 类型名 或 new[]类型名从右向左C++专有
2delete/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从右到左两种语言都有
16throw抛异常单目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:前缀自增/减运算 和 后缀自增/减运算

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

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

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

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

首先,来看代码:

void test001()
{
    int a = 0;
    ++a--;//语法错误:++需要左值
}

void test002()
{
    int a=0;
    a = !a++;
    printf("a=%d\n",a);//结果输出1 不论是在VS环境还是GCC环境
}

void test003()
{
    int a=0;
    a = a+a++;
    printf("a=%d\n",a);//结果输出1 不论是在VS环境还是GCC环境
}

void test004()
{
    int a=0;
    a = 2+a++;
    printf("a=%d\n",a);//结果输出3 不论是在VS环境还是GCC环境
}
从上面的代码可以很明显的看出,如果认为后缀自增运算有所谓的高优先级,那么完全就说不通

首先++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 请用圆括弧安排好优先级——只有这个是唯一可靠的伙伴!

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
















  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值