运算符
认识了C++的基本数据类型后,接下来就要操作这些数据了。而要操作数据,就离不开运算符和表达式。包括赋值运算、算数运算、关系运算、逻辑运算、逗号运算、位运算、移位运算、sizeof运算等。
1. 表达式
C++中,表达式由运算符、操作数、括号等组成。简单的表达式只包含一个数值,复杂的表达式包含多个操作数和多个运算符。
C++中的运算符多数继承自C语言,新增的运算符仅有“::”(作用域运算符)和“->”(指向运算符)。根据操作数的数量,运算符可分为单目运算符、双目运算符和三目运算符。
复杂表达式中,运算符执行的先后顺序由它们的优先级和结合性决定。表达式的值的数据类型由运算符的种类和操作数的数据类型决定。
表达式可出现在赋值语句的右侧或函数的参数中。表达式可返回一个结果,其数据类型取决于组成表达式的变量和常量的类型。
程序中常常遇到的赋值符号“=”就是赋值运算符,其作用就是将一个数据赋值给一个变量。
int age;
age = 10;
赋值表达式的一般形式为:数据类型 变量名 = 表达式。
2. 算数运算符
算数运算符包括4个单目运算符和5个双目运算符。
运算符 | 功能 | 用法 | 示例 |
---|---|---|---|
+ | 取正值 | --- | +520 |
+ | 加法运算 | x + y | 1 + 2 |
- | 取负值 | --- | -520 |
- | 减法运算 | x - y | 5 - 2 |
* | 乘法运算 | x * y | 2 * 5 |
/ | 除法运算 | x / y | 9 / 3 |
% | 取模运算 | x % y | 5 % 2 |
++ | 自增 | x++、++y | x++、++y |
-- | 自减 | x--、--y | x--、--y |
说明
x,y表示使用运算符的对象,可以是表达式、变量或常量。
表达式求值时,会按照运算符的优先级别从高到低依次执行。单目正和单目负的优先级最高;“*”、“/”、“%”的优先级高于“+”、“-”;括号在所有运算符中优先级最高,可以改变运算顺序。当运算符的优先级相同时,结合方向为“自左向右”。这和数学中的计算顺序是一样的。
3. 算数表达式
使用算数运算符的表达式称为算数表达式。
(3 + 5) / Rate;
Top - Bottom + 1;
Height * Width;
进行四则混合运算时,不同的数据类型会先自动转化成同一类型,然后再进行运算。
1、若所有操作数的数据类型相同,则表达式运算结果的数据类型和操作数的数据类型相同。例如,两个整型数相加,其结果仍然是一个整型数。
2、若操作数的数据类型不同,则表达式运算结果的数据类型取最高的数据类型,以保证数据精度不会发生损失。例如,混合运算10 + 'a' - 1.5 + 3.2 * 6
中出现了整型、字符型、浮点型,表达式的运算结果是浮点型。
3.1 自增、自减
自增运算符“++”和自减运算符“--”对变量的操作效果分别是使用变量增加1和减少1.
自增、自减运算符可以放在变量前面,如++x
,这种情况称为前缀运算;也可以放在变量的后面,如x--
,这种情况称为后缀运算。两者对运算结果的影响是不一样的。
- 前缀运算中,变量先完成自增或自减运算,再以增减后的结果参与表达式运算
- 后缀运算中,变量先参加表达式运算,之后再进行自增或自减
当自增、自减运算符出现在表达式内部,作为运算的一部分时,前缀和后缀的运算结果差异很大。
#include <iostream>
int main() {
int x = 10;
int y = 20;
int a, b;
a = x++; // x先赋值再自增,a为10
b = ++y; // y先自增在复制,b为21
std::cout << a << std::endl;
std::cout << b << std::endl;
return 0;
}
小练习: 计算基本收益,某基金年利率为3.5%,现存入1万元本金,问一天后连本带利有多少钱。计算公式:一天收益 = 本金 * 年化利率 / 365;
解析
#include<iostream>
int main() {
double p = 3.5 / 100; // 年利率
int x = 10000; // 本金
double result = x + x * (1 * p / 365); // 最终收益
std::cout << result << std::endl;
return 0;
}
4. 关系运算符
关系运算符包括大于、大于或等于、小于、小于或等于、等于和不等六种运算符。
运算符 | 描述 | 用法 | 说明 |
---|---|---|---|
> | 大于 | x > y | 前者大于后者,运算结果为真,否则为假 |
< | 小于 | x < y | 前者小于后者,运算结果为真,否者为假 |
>= | 大于或等于 | x >= y | 前者大于或等于后者,运算结果为真,否则为假 |
<= | 小于或等于 | x <= y | 前者小于或等于后者,运算结果为真,否则为假 |
== | 等于 | x == y | 前者等于后者,运算结果为真,否则为假 |
!= | 不等于 | x != y | 前者不等于后者,运算结果为真,否则为假 |
4.1 关系表达式
关系表达式的一般形式是:表达式1 关系运算符 表达式2。
关系运算符可对两个表达式进行比较,返回一个真值或假值。真值为1假值为0。
7 > 5; // 7大于5表达式成立,表达式结果为真
3 > 4; // 3大于4表达式不成立,表达式结果为假
'a' > 'b' // 比较之前会将'a/b'转化为对应的ASCII码值,该关系成立,表达式结果为真
关系运算符的优先级低于算数运算符,高于赋值运算符。>、>=、<、<=
的优先级相同,高于==、!=
。
误区
要注意区分“=”和“==”,这两个运算符的含义截然不同。“=”是赋值运算符,用于将等号右边的值或表达式的结果赋值给左边;“==”是等于运算符,用于判断左右两侧的值是否相等。
5. 逻辑运算符
逻辑运算符可根据表达式的真假属性返回真值或假值。C++中,表达式的值非0,表达式其值为真,非0的值参与逻辑运算,等价于1;假值总是0。
逻辑运算符可对真和假这两种逻辑值进行运算,运算后的结果仍然是一个逻辑值。逻辑运算符有三个分别是逻辑与、逻辑非、逻辑或。
运算符 | 功能 | 用法 | 说明 |
---|---|---|---|
&& | 逻辑与 | x && y | 两个表达式都为真时,逻辑运算的结果为真,否则为假。 |
|| | 逻辑或 | x || y | 两个表达式有一个为真,逻辑运算的结果为真,两个表达式都为假时,逻辑运算的结果为假。 |
! | 逻辑非 | !x | 表达式为真,逻辑运算结果为假,表达式为假,逻辑运算结果为真。 |
逻辑运算符,可以将多个关系表达式的结果合并在一起进行判断。逻辑表达式的值则是各种逻辑运算的最后值。
1、逻辑运算符两侧的表达式结果,除了可以是0和非0的整数外,还可以是其他任何类型的数据。
2、逻辑运算符两侧的表达式,并不都需要进行判定或求值。
- 对于逻辑与运算,如果表达式1判定为假,系统不再判定或求解表达式2。
- 对于逻辑或运算,如果表达式1判定为真,系统不再判定或求值表达式2。
小练习:
某公司要求应聘年龄在20~30岁之间。假设有一名应聘者,年龄为32岁,判断他是否满足公司的应聘条件。
#include<iostream>
int main() {
int age = 32;
if(20 <= age && age <= 30) {
std::cout << "满足公司应聘需求" << std::endl;
}else {
std::cout << "不满足公司应聘需求" << std::endl;
}
return 0;
}
6. 逗号运算符
C++中,逗号运算符“,”用于在单个语句中同时执行多个操作,并返回最后一个表达式的值。逗号运算符的优先级最低,结合方向为自左向右。
逗号表达式又称为顺序求值表达式,一般形式是:表达式1,表达式2,...,表达式n。
说明
程序中使用逗号表达式,通常是为了取得各个表达式的值,而不是求解整个逗号表达式的值。也就是说,逗号运算符最大的作用就是把多个表达式“串联”起来。逗号运算符常用于for循环语句中。
7. 位运算符
C++中,位运算的对象只能是整型和字符串型数据。位运算符可对其二进制位进行操作,包括位与、位或、位异或、取反四种运算。
运算符 | 功能 | 用法 |
---|---|---|
& | 位与 | x & y |
| | 位或 | x | y |
^ | 位异或 | x ^ y |
~ | 取反 | ~x |
“&”的优先级最高、“|”次之、“~”最低。
7.1 位与运算
位与运算中,需要将操作数转化为二进制表示方式,从低位(最右边)到高位对齐,逐位求与。若两个操作数对象同一位都是1,则结果为1,否则结果为0。
例如,12与8位与,最后得到的结果是8。

7.2 位或运算
位或运算中,需要将操作数转化为二进制表示方式,从低位(最右边)到高位对齐,逐位求或。若两个操作数对象同一位都是0,则结果返回0,否则结果返回1。
例如,4和8位或运算的结果是12。

7.3 位异或运算
位异或运算中,需要将两个操作数转化为二进制表示方式,从低位(最右边)到高位对齐,逐位求异或。若两个操作数对象同一位不相同,则结果对应位为1,否则结果中对应位为0。
例如,31和22位异或后得到的结果是9。

7.4 取反运算
取反运算中,将操作数转化为二进制表示方式,然后将各二进制1变为0,0变为1。
例如,41883取反运算后得到的结果是23652。

说明
十进制数在用二进制表示时有原码、反码、补码等表示方式。
8. 移位运算符
C++中,移位运算的对象只能是整型或字符串型数据。移位运算符有两个,分别是左移(<<)和右移(>>)。例如,a << 2表示将变量a的二进制左移2位。
8.1 左移运算
左移运算,将一个二进制数向左移动一定的位数,左边(最高位)溢出的位会被丢弃,右边(最低位)的空间用0补齐。
当数值不存在溢出风险时,左移运算的效果相当于原始数乘2的幂。

当需要移位的数值较大时,会发生数据溢出并被舍去,例如,操作数41883,左移1位会变成18230,左移2位会变成,36460。

8.2 右移运算
右移运算,指将一个二进制数向右移动指定的位数,右边(最低位)溢出的位被丢弃,左边(最高位)的空用0补齐。或者用被移位操作数的符号填充。运算结果和编译器有关,在使用补码的机器中,整数的符号为是0,负数的符号位是1。
当数值不存在溢出风险时,右移运算的效果相当于原数除以2的幂。
8.3 示例
1、定义一个变量并赋值0x40(对应十进制64),将这个值进行左移一位运算。
展开示例
#include<iostream>
int main() {
int x = 0x40;
int y = x << 1;
std::cout << y << std::endl; // 结果是128
return 0;
}
由于位运算速度很快,在程序中遇到需要乘或除2的幂的情况,一般采用位运算来代替。
2、定义一个长整型并赋值,利用位与和移位运算,计算这个十六进制的高4位和低4位。
展开示例
#include<iostream>
int main() {
long x = 0x12345678;
int y;
y = x & 0xFFFF; // 与0xFFFF位与,得到最低4位
printf("最低4位是:0x%x\n", y);
y = (x & 0xFFFF0000) >> 16; // 与0xFFFF0000位与,得到高4位
printf("最高4位是:0x%x\n", y);
return 0;
}
9. 复合赋值运算符
相当于一个简单赋值运算符和其他运算符的组合。C++中提供了很多复合赋值运算符。
操作符 | 功能 | 用法 | 说明 |
---|---|---|---|
+= | 加法赋值 | x+=y | 等价于x = x + y |
-= | 减法赋值 | x-=y | 等价于x = x - y |
*= | 乘法赋值 | x*=y | 等价于x = x * y |
/= | 除法赋值 | x/=y | 等价于x = x / y |
%= | 取余赋值 | x%=y | 等价于x = x % y |
<<= | 左移赋值 | x<<=y | 等价于 x = x << y |
>>= | 右移赋值 | x>>=y | 等价于 x = x >> y |
&= | 按位与运算赋值 | x&=y | 等价于 x = x & y |
|= | 按位或运算赋值 | x|=y | 等价于 x = x | y |
^= | 按位异或运算赋值 | x^=y | x = x ^ y |
复合赋值运算符不但书写形式简洁、紧凑、运算也非常高效,编译器在生成代码时能够直接进行优化。
复合赋值运算符可将中间的运算结果返回,作为表达式的值。
int x = 2;
x *= 2; // x 被赋值为 4
10. sizeof运算符
sizeof运算符用于计算指定数据类型或表达式结果在内存中占用的字节数,有两种语法形式。
sizeof(类型说明符);
sizeof(表达式);
sizeof(char); // 返回1,说明char类型占用1个字节
sizeof(12); // 返回4,说明int整型占用4个字节
警告
由于CPU寄存器的位数不同,不同计算机上同种数据类型占用的内存字节大小可能不同。
11. 运算符的优先级和结合性
运算符的优先级决定了表达式中各项运算执行的先后顺序,高优先级的运算符要先于低优先级的运算符进行运算。运算符的结合方式有两种:自左向右结合和自右向左结合。自左向右结合表示运算符优先与其左边的表达式结合,如加法、减法、乘法、除法运算等;自右向左结合表示运算符优先与其右边的表达式结合,如自增、自减、复合赋值运算等。
同一优先级的运算符,运算顺序由结合方向决定。如:表达式2*3/3
中,“*”和“/”的优先级相同,其结合方向自左向右。
运算符 | 名称 | 优先级 | 结合性 |
---|---|---|---|
() [] -> . | 圆括号 下标运算符 指向运算符(取类或结构分量) 成员运算符(取类或结构成员) | 1(最高) | → |
! ~ ++ -- - | 逻辑非运算符 按位取反运算符 自增运算符 自减运算符 取负运算符 | 2 | ← |
& * (类型) sizeof | 取地址运算符 取内容运算符 强制类型转化 计算长度运算符 | --- | ← |
* / % | 乘法运算符 除法运算符 取余运算符 | 3 | → |
+ - | 加法运算符 减法运算符 | 4 | → |
<< >> | 左移位运算符 右移位运算符 | 5 | → |
< <= > >= | 小于运算符 小于等于运算符 大于运算符 大于等于运算符 | 6 | → |
== != | 等于运算符 不等运算符 | 7 | → |
& | 位与运算符 | 8 | → |
^ | 位异或运算符 | 9 | → |
| | 位或运算符 | 10 | → |
&& | 逻辑与运算符 | 11 | → |
|| | 逻辑或运算符 | 12 | → |
?: | 条件运算符 | 13 | ← |
= /= %= *= -= >>= <<= &= |= ^= | 简单赋值运算符 除法赋值运算符 取模赋值运算符 乘法赋值运算符 减法赋值运算符 右移位赋值运算符 左移位赋值运算符 按位与赋值运算符 按位或赋值运算符 按位异或赋值运算符 | 14 | ← |
, | 逗号运算符 | 15 | → |
12. 数据类型转化
变量的数据类型转化方式有两种,一种是隐式类型转化(自动转化),一种是强制类型转化。
12.1 隐式类型转化
隐式类型转化发生在不同数据类型的量进行赋值或混合运算时,由编译系统自动完成,并遵循以下规则:
- 赋值表达式中,“=”右边量的数据类型会自动转化为左边量的数据类型。如果右边量的数据类型的精度比较高,则赋值后会降低精度,丢失一部分数据(丢失部分按“四舍五入”规则向前舍入)。
- 算数表达式中,若参与运算的多个量的数据类型不同,则会先转化成统一类型,然后再进行运算。转化的规则是:将数据由低精度转化为高精度,保证精度不丢失。
- int类型与long类型混合运算时,先把int型转化为long型再进行计算。
- 浮点数都是以double类型进行计算的,哪怕是仅含float类型,也会先转化为double类型再计算。
- char类型和short类型运算时,先转化成int型再计算。
警示
C++中,整数的默认类型时int,浮点数的默认类型时double。如果一个计算中数字都是整数,则计算结果默认也是int。但是这样有时会出现意外的错误。
计算:double x = 5 / 2;
计算后会发现b的值是2.0,而不是2.5。要想得到正确的答案,应改为:
double x = 5.0 / 2; 把其中一个数改为浮点数,保证精度。
12.2 强制类型转化
强制类型转化是通过类型转化说明符来实现的。
类型说明符(表达式)、(类型说明符)表达式。
其功能是将表达式的结果强制转化成类型说明符所表示的类型。例如,“(float)x”表示把x强制转化为“float”类型;(int)(x + y)表示把“x + y”的结果强制转化为int型。
强制类型转化不改变数据定义时对该变量定义的类型。下面代码中x仍然是double类型。
double x;
(in)x;
警告
如果要对某个表达式进行强制类型转化,需要用括号将表达式括起来,否则将只对表达式中的第一个变量或常量进行强制类型转化。
13. 判断左值与右值
C++中,表达式语句可以分为左值和右值。左值是内存中持续存储的数据,表达式执行完毕后会持久存在;右值是临时存储的计算结果,表达式执行完毕之后就不再存在。
程序中声明的独立变量都是左值。
int x;
short y;
char z;
下述代码中,x、y、z都是左值;z - y是一个储存减法结果的临时数据,是一个右值;x++实际上是编译器中一个临时变量执行了自增,是一个右值;++x正好相反,是直接对x进行自增,是一个左值;同理,z--是一个右值。
int x = 0;
int y = 1;
int z = 2;
x = z - y;
y = x++;
z = ++x;
z--;
注意:若表达式的结果不是一个左值,那么一定是一个右值。
说明
后缀自增、自减运算中,编译器先生成一个变量的临时复制,再对其进行自增或自减,最后返回临时复制内容,因此“x++”、“x--”是右值。前缀自增、自减运算中,直接对“x”进行自增、自减并返回“x”,因此是左值。
💬 欢迎评论!请确保您已登录 GitHub。