运算符和表达式
一个操作数(可以是常量,也可以是一个标识符),就可以构成一个最简单的表达式。将多个操作数组合在一起,就可以构成复合的表达式。运算符(也可以叫做操作符)可以组合多个操作数。因此,也可以认为通过运算符,可以构成表达式。
如果,运算符只需要一个操作数,那么就是一元运算符,也可以叫做单目运算符。如果需要两个操作数,那么就是二元运算符,也可以叫做二目运算符。如果需要三个操作数,那么就是三元运算符,也可以叫做三目运算符。
运算符具有优先级和结合性。优先级高的运算符先运算,同等优先级的运算符按照结合性进行运算。特别强调的是,运算符的优先级对于大多数人来说,都是搞不清楚的。所以,在这种情况下,多使用括号就行了。多使用括号不是什么丢人的事情,因为即使你自己能搞清楚复杂的运算符的优先级情况,别人也不见得能搞的懂。也就是说,你有能力可以写出太过优秀的代码反而会给别人造成困扰。
在c++中,大多数的运算符都是可以重载的,不过这并不是现在的重点。关于运算符的重载,会在后边的章节中详细提到。
算数运算符
基本的算数运算符就是我们最常使用的算数运算。有的时候自增自减运算符和位运算符也可以被当成是广义上的算数运算符。不过我并没有采用这样的划分。
通过算数运算符和操作数组成的表达式,又被叫做算数表达式。
注:说到这里,其实我不太清楚我是否有必要详细阐述这些名词和概念,因为这样做会使得教程变的晦涩难懂。但是,当我在写这样一份教程的时候,我很难不去这样做。因为太过随意的使用词语,可能会出现很多错误,也会给不懂的阅读者造成错误的印象。所以,每当我使用一个词语的时候,我都会特别小心谨慎,但是,其实我感觉没必要这样。
基本算数运算符
一元加:+
一元减:-
加:+
减:-
乘:*
除:/
模:%
1,所谓一元加和一元减就是我们平常常说的正号和负号。
2,求模就是求两个数相除的余数。
3,c++中没有整除运算符,也就是其他语言中可能会见到的//。但是如果两个整型的数据相除的话,得到的结果也是整型,相当于是舍弃小数部分,(注意:不是四舍五入,在c++11以后均采用向零取整)。
4,算数运算符都只会计算结果,不会修改操作数(这里说的是非重载的原本的算数运算符)。
5,这些算数运算符的优先级顺序和数学中的优先级顺序是一样的,也就是先乘除,再加减。
6,%的优先级和/的优先级是一样的,而且%的操作数应当是整数。
类型提升
当运算符操作的操作数,类型不同的时候,为了能解决这种类型不同的问题,因此会对其进行类型提升(也可以叫做类型转换)。
注意:类型提升的规则繁琐而复杂,如果你没兴趣知道的话,可以跳过。只需注意使用时避免出现类型不同的情况即可。
整型提升:表达式计算时能被转换为整型的操作数,将提升为整型,其中bool类型的true会被提升为1,false会被提升为0。可整型提升的类型包括无作用域枚举,不包括作用域枚举。
c++20补充规则:如果有一个操作数是枚举类型,另外一个操作数是其他的枚举类型或者浮点类型,那么放弃整型提升。
类型提升规则(按照以下规则顺序执行)
规则1:如果有一个操作数为long double,另一操作数提升为long double。
规则2:如果有一个操作数为double,另一操作数提升为double。
规则3:如果有一个操作数为float,另一操作数提升为 float。
规则4:如果都不是,那么操作数应该已经完成了整型提升。
规则5:如果此时操作数均为有符号或者均为无符号,那么较小等级的操作数将转换为较大等级的操作数类型。
规则6:如果无符号的操作数等级大于或者等于有符号的操作数的等级,那么将有符号操作数的等级转换为无符号的操作数的等级。
规则7:如果有符号的操作数等级足够大,能够表示全部无符号的操作数等级的类型,那么将无符号的操作数等级转换为有符号操作数的等级。
规则8:如果有符号的操作数等级大于无符号操作数的等级,但是没办法表示该无符号数的所有数值范围,那么将两个数都转换为有符号数等级的无符号数版本。
整型等级:bool,char,short,int,long,long long。其中无符号等级与有符号等级相同。char8_t,char16_t,char32_t和wchar_t的等级等于其对应类型的等级。
如果最终计算结果发生溢出,无符号类型将会重新来过(从0重新开始),有符号类型的行为未知。
注意:溢出行为的发生可能会受到编译器的影响。也就是说,即使按照正确的运算顺序计算,每一步都不会发生溢出,但如果改变运算的优先级顺序,且不会影响计算的最终结果,那么在这种情况下如果可能会导致溢出的状况,那么就有可能发生溢出。这是由于编译器的优化导致的。