跳转到内容

运算符

认识了C++的基本数据类型后,接下来就要操作这些数据了。而要操作数据,就离不开运算符和表达式。包括赋值运算、算数运算、关系运算、逻辑运算、逗号运算、位运算、移位运算、sizeof运算等。

1. 表达式

C++中,表达式由运算符、操作数、括号等组成。简单的表达式只包含一个数值,复杂的表达式包含多个操作数和多个运算符。

C++中的运算符多数继承自C语言,新增的运算符仅有“::”(作用域运算符)和“->”(指向运算符)。根据操作数的数量,运算符可分为单目运算符、双目运算符和三目运算符。

复杂表达式中,运算符执行的先后顺序由它们的优先级和结合性决定。表达式的值的数据类型由运算符的种类和操作数的数据类型决定。

表达式可出现在赋值语句的右侧或函数的参数中。表达式可返回一个结果,其数据类型取决于组成表达式的变量和常量的类型。

程序中常常遇到的赋值符号“=”就是赋值运算符,其作用就是将一个数据赋值给一个变量。

cpp
int age;
age = 10;

赋值表达式的一般形式为:数据类型 变量名 = 表达式。

2. 算数运算符

算数运算符包括4个单目运算符和5个双目运算符。

运算符功能用法示例
+取正值---+520
+加法运算x + y1 + 2
-取负值----520
-减法运算x - y5 - 2
*乘法运算x * y2 * 5
/除法运算x / y9 / 3
%取模运算x % y5 % 2
++自增x++、++yx++、++y
--自减x--、--yx--、--y

说明

x,y表示使用运算符的对象,可以是表达式、变量或常量。

表达式求值时,会按照运算符的优先级别从高到低依次执行。单目正和单目负的优先级最高;“*”、“/”、“%”的优先级高于“+”、“-”;括号在所有运算符中优先级最高,可以改变运算顺序。当运算符的优先级相同时,结合方向为“自左向右”。这和数学中的计算顺序是一样的。

3. 算数表达式

使用算数运算符的表达式称为算数表达式。

cpp
(3 + 5) / Rate;
Top - Bottom + 1;
Height * Width;

进行四则混合运算时,不同的数据类型会先自动转化成同一类型,然后再进行运算。

1、若所有操作数的数据类型相同,则表达式运算结果的数据类型和操作数的数据类型相同。例如,两个整型数相加,其结果仍然是一个整型数。

2、若操作数的数据类型不同,则表达式运算结果的数据类型取最高的数据类型,以保证数据精度不会发生损失。例如,混合运算10 + 'a' - 1.5 + 3.2 * 6中出现了整型、字符型、浮点型,表达式的运算结果是浮点型。

3.1 自增、自减

自增运算符“++”和自减运算符“--”对变量的操作效果分别是使用变量增加1和减少1.

自增、自减运算符可以放在变量前面,如++x,这种情况称为前缀运算;也可以放在变量的后面,如x--,这种情况称为后缀运算。两者对运算结果的影响是不一样的。

  • 前缀运算中,变量先完成自增或自减运算,再以增减后的结果参与表达式运算
  • 后缀运算中,变量先参加表达式运算,之后再进行自增或自减

当自增、自减运算符出现在表达式内部,作为运算的一部分时,前缀和后缀的运算结果差异很大。

cpp
#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;

解析
cpp
#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。

cpp
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岁,判断他是否满足公司的应聘条件。

cpp
#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),将这个值进行左移一位运算。

展开示例
cpp
#include<iostream>
int main() {
	int x = 0x40;
	int y = x << 1;
	std::cout << y << std::endl; // 结果是128
	return 0;
}

由于位运算速度很快,在程序中遇到需要乘或除2的幂的情况,一般采用位运算来代替。

2、定义一个长整型并赋值,利用位与和移位运算,计算这个十六进制的高4位和低4位。

展开示例
cpp
#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^=yx = x ^ y

复合赋值运算符不但书写形式简洁、紧凑、运算也非常高效,编译器在生成代码时能够直接进行优化。

复合赋值运算符可将中间的运算结果返回,作为表达式的值。

cpp
int x = 2;
x *= 2;   // x 被赋值为 4

10. sizeof运算符

sizeof运算符用于计算指定数据类型或表达式结果在内存中占用的字节数,有两种语法形式。

cpp
sizeof(类型说明符);
sizeof(表达式);
cpp
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类型。

cpp
double x;
(in)x;

警告

如果要对某个表达式进行强制类型转化,需要用括号将表达式括起来,否则将只对表达式中的第一个变量或常量进行强制类型转化。

13. 判断左值与右值

C++中,表达式语句可以分为左值和右值。左值是内存中持续存储的数据,表达式执行完毕后会持久存在;右值是临时存储的计算结果,表达式执行完毕之后就不再存在。

程序中声明的独立变量都是左值。

cpp
int x;
short y;
char z;

下述代码中,x、y、z都是左值;z - y是一个储存减法结果的临时数据,是一个右值;x++实际上是编译器中一个临时变量执行了自增,是一个右值;++x正好相反,是直接对x进行自增,是一个左值;同理,z--是一个右值。

cpp
int x = 0;
int y = 1;
int z = 2;
x = z - y;
y = x++;
z = ++x;
z--;

注意:若表达式的结果不是一个左值,那么一定是一个右值。

说明

后缀自增、自减运算中,编译器先生成一个变量的临时复制,再对其进行自增或自减,最后返回临时复制内容,因此“x++”、“x--”是右值。前缀自增、自减运算中,直接对“x”进行自增、自减并返回“x”,因此是左值。

💬 欢迎评论!请确保您已登录 GitHub。