算数运算是值数值类型进行的值的运算,数值类型包括整数类型、浮点数类型以及被识别为这两种类型的字面量。数据类型决定了该变量或者字面量能进行哪些运算、如何进行运算。而使用运算符则是要对该操作数实际进行该运算,如果是该类型支持的运算符则能得到对应的结果,否则编译出错。
除了数组,之前提到的所有数据类型归根结底只是整数和浮点数,而整数和浮点数最常进行的运算就是算数运算。
C/C++提供了多种算数运算符:
运算符 | 作用 |
---|---|
+ | 两个操作数相加 ,得到和 |
- | 两个操作数相减 ,得到差 |
* | 两个操作数相乘 ,得到积 |
/ | 两个操作数相除 ,得到商 |
% | 两个操作数相除,得到余数 |
++ | 操作数自增1 |
– | 操作数自减1 |
操作数能进行哪些运算、具体如何运算、运算的结果是何种类型,这都是由参与运算的操作数决定的。
1.整数之间的运算:
两个整数之间可以进行加、减、乘、除、取余、自加、自减。
运算的结果是操作数中宽度较大的操作数的类型(小宽度操作数先扩展)、signed和unsigned类型同时参与运算则结果为unsigned类型,如果结果超出较大的数据宽度也不会自动扩展。对于加法和减法,这样的规则可能会导致结果溢出,例如:
unsigned long a = 4294967295;
int b = 1;
unsigned long us = a + b; // us中存放0x 00 00 00 00
因为unsigned long占4字节,4294967295为0x FF FF FF FF,int类型的1扩展为0x 00 00 00 01,二者相加结果为0x 01 00 00 00 00由于不会自动扩展,仍为4字节,所以仅仅保留了低位。
查看反汇编,与分析结果一致:
同样地,也不会自动缩小数据宽度:
long long a = 4294967296;
int b = 1;
long long us = a - b; // us 中存放0x00 00 00 00 FF FF FF FF
// 如果用unsigned接收该结果则会截断高位保留低位
减法运算实际上是a中二进制串加上b中二进制串的补码,通过反汇编可以查看,一个8字节的数要使用两次SUB指令分别对高字节和低字节运算:
加法和减法要注意溢出问题,乘法和除法也要注意。同时除法要避免除0错误,并且两个整数不能整除时结果只保留商的整数部分,因为结果的类型必定是二者之一不会是浮点数,例如:
int a = 10;
int b = 3;
float us = a / b; // a / b的结果仍然是int类型,只保留整数部分3,us的值为3.0
除此之外,任何被识别为整数的字面量也可以与整数一样进行算数运算,例如:
int a = 10;
int b = 10 + 'a'; // 加a的ASCII码值
// 虽然字符串字面量的值是整数型的地址,但只能和整数做加法,不能进行其他运算
2.浮点数之间的运算
前面提到,浮点数的存储大多数是不精确的,也就是实际存储的值和赋值给变量的字面值不一定完全相等,存在精度的损失。
两个浮点数之间运算,结果也是二者数据宽度较大的类型。double和long double运算时结果是long double。
浮点数的运算也会溢出只是超出浮点数的表示范围,而不是二进制串被截断,因为浮点数本身就是按照IEEE编码规则存储的二进制串,指数和尾数部分结合表示浮点数,即使是4字节的float类型可以存储很大或很小的浮点数。
浮点数运算仍然会有精度的损失,因为两个浮点数本身会损失精度、运算的结果可能再次损失精度。
3.整数和浮点数运算
整数和浮点数运算,其结果一定是操作数中浮点数的类型。
即使是long long类型的整数和float类型的浮点数运算,其结果仍然会是4字节的float而不是8字节的long long或double。
修改之前整数除法的例子:
10 / 3; // 结果为int类型3
10.0 / 3; // 结果为double类型3.3...
10 / 3.0F; // 结果为float类型3.3...
4.自增自减运算符
自增自减运算符只需要一个操作数,对操作数自身进行加1或减1。这个操作数必须时可改变的值(变量),可以是整数变量也可以是浮点数变量。
自增自减运算符作用于操作数左边和操作数右边在某些情况下的作用是不同的,例如:
如果单独使用,则在左边和右边效果相同:
int a = 10;
a++;
int a = 10;
++a;
结果都是将a自增1,a最终的值为11。
如果将自增自减这个表达式参与运算或赋值,则左右有区别:
int a = 10;
int b = 10;
int aa = ++a; // a先自增为11,再赋值给aa,aa的值为11
int bb = b++; // b先赋值给bb值为10,b的值再自增为11
查看反汇编:
二者只是赋值和自增的先后顺序不同,通常可理解为若自增自减表达式参与运算或赋值,则在左边先自增自减然后用变化后的值参与运算或赋值,在右边则先用原值参与运算或赋值再进行自增自减。
特别注意,只有变量才能使用自增自减运算符,对常量使用则报错:
10++;
++10;
// 表达式必须是可修改的左值
5.取余运算符
取余运算符只能用于两个整数之间(可以是正整数、负整数或0),如:
5 % 2; //值为1
-5 % -2; //值为-1
5 % -2; //值为1
-5 % 2; //值为-1
0 % 2; //值为0
0 % -2; //值为0
2 % 0; // 除0错误
余数的符号和前面被除数的符号一致