信奥竞赛

系列

剑指信奥 | C++ 之运算

剑指信奥 | C++ 之运算

C++ 的运算

今天,我们系统的学习一下 C++ 中的各类运算符。

运算是编程中最为常见的操作,也是构成或易或难算法的基础。

在 C++ 中,有着各种各样的运算符,我们可以根据实际的需要灵活的选择。按照运算的方式,人们把运算符分为了这样的几个类别:

  1. 算术运算符
  2. 赋值运算符
  3. 比较运算符
  4. 逻辑运算符
  5. 按位运算符
  6. 其他运算符

我们将对每一类运算符进行详细的说明,并配以精简的代码示例。

算术运算符

算术运算符,顾名思义,就是指代常规的算术运算,我们日常加减乘除就在此类,但 C++ 还提供了其他几种稍微特殊的运算。

我们看下算术运算符的分类:

  1. + 加法
  2. - 减法
  3. * 乘法
  4. / 除法
  5. % 取模
  6. ++ 自增
  7. -- 自减

前四种运算都是比较常见的,我们先看一下这四种运算的示例:

#include <iostream>

using namespace std;

int main() {

int x = 1, y = 2;

cout << "x + y = " << x + y << endl;
cout << "x - y = " << x - y << endl;
cout << "x * y = " << x * y << endl;
cout << "x / y = " << x / y << endl;

return 0;
}

/*
output:
x + y = 3
x - y = -1
x * y = 2
x / y = 0
*/

特别需要注意的是,在 C++ 中,整数除法的运算规则是截去余数,如果希望得到精确的结果,需要使用浮点数进行除法运算。

我们发现,算术运算符的后面三种运算并不常见,我们逐一解释一下。

一个百分号 % 表示的是 取模 mod 运算,结果是两个数相除后的余数:

#include <iostream>

using namespace std;

int main() {

int x = -1, y = 2;

cout << "x % y = " << x % y << endl;

return 0;
}

/*
output:
x % y = -1
*/

关于取模运算,需要注意以下几点:

  1. 取模运算仅支持整数
  2. 第二个操作数不能为 0
  3. 正负与第一个操作数相同

虽然取模运算只能使用整数,但浮点数也可以进行取模运算,使用的是 cmath 库的 fmod() 函数。

取模运算是一种在算法中比较常见的运算,比如判断奇偶数,判断素数的试除法,求最大公约数的欧几里得算法等,都是使用取模运算的场景。

自增 ++ 和自减 -- 可以放在一起,作用是把操作数在原有基础上加 1 或减 1。

#include <iostream>
#include <cmath>

using namespace std;

int main() {

int x = 1;
double y = 2.3;

cout << "x++ = " << x++ << endl; // 后加
cout << "x = " << x << endl;
cout << "--y = " << --y << endl; // 先减

return 0;
}

/*
output:
x++ = 1
x = 2
--y = 1.3
*/

需要注意的是,自增或自减运算符有 先后 的区别,方式是把运算符放在操作数的前面或是后面,在前是立即运算,再执行当前指令,在后是先执行当前指令,再做运算。

比较运算符

C++ 语言中的比较运算,一般用于比较两个数值的大小关系,有以下几种:

  1. == 等于
  2. != 不等于
  3. > 大于
  4. < 小于
  5. >= 大于或等于
  6. <= 小于或等于

比较运算符的规则比较简单,它们的作用主要是在选择结构中作为判断条件使用的,比较的结果只有两种,即真或假。

含有比较运算符的表达式也常用作逻辑运算的操作数,我们可以参看下一节的示例,

逻辑运算符

逻辑运算与比较运算有一个相同点,就是结果类似,非真即假,但是操作数类型不同。

逻辑运算有以下几种:

  1. && 与运算
  2. || 或运算
  3. ! 非运算

与运算的规则是两个操作数均为真,结果才为真,否则都为假;

或运算的规则是两个操作数均为假,结果才为假,否则都为真;

非运算的规则是取反运算,即非真为假,以及非假为真。

代码示例:

#include <iostream>

using namespace std;

int main() {

int x = 1, y = 2;

cout << (x > 1 && y >= 2) << endl;
cout << (x > 1 || y >= 2) << endl;
cout << !(x > 1 || y >= 2) << endl;

return 0;
}

/*
output:
0
1
0
*/

这里的输出结果,0 代表 false 假,1 代表 true 真。

按位运算符

按位运算应该是所有运算符里比较特殊的一种运算了,它是把整数以二进制的方式按位进行操作,主要的运算符有:

  1. & 按位与
  2. | 按位或
  3. ~ 按位非
  4. ^ 按位异或
  5. << 按位左移
  6. >> 按位右移

按位运算前四种运算的规则如下表所示:

xyx & yx | yx ^ y~x
000001
010111
111100
100110

按位运算的左移和右移,运算规则是把左操作数的二进制值向左或向右移动右操作数指定的位数。

我们看一份代码示例:

#include <iostream>

using namespace std;

int main() {

int x = 1; // 二进制 00000001
int y = 2; // 二进制 00000010

cout << "x & y = " << (x & y) << endl; // 00000000
cout << "x | y = " << (x | y) << endl; // 00000011
cout << "~x = " << ~x << endl; // 11111110
cout << "x ^ y = " << (x ^ y) << endl; // 00000011
cout << "x << 2 = " << (x << 2) << endl; // 00000100
cout << "x >> 2 = " << (x >> 2) << endl; // 00000000

return 0;
}

/*
output:
x & y = 0
x | y = 3
~x = -2
x ^ y = 3
x << 2 = 4
x >> 2 = 0
*/

赋值运算符

赋值运算符,我们一直在使用,就是为变量赋值时使用的等号:=,用法也很简单,但是,在我们学习了其他的一些运算符后,一部分运算符可以和等号放在一起,形成 二元赋值运算符 不要被这个词吓到,它的含义很简单,就是一种关于赋值的简便写法。

所有的赋值运算符如下:

  1. = 赋值
  2. += x += 1 等价于 x = x + 1
  3. -= x -= 1 等价于 x = x - 1
  4. *= x *= 1 等价于 x = x * 1
  5. /= x /= 1 等价于 x = x / 1
  6. %= x %= 1 等价于 x = x % 1
  7. &= x &= 1 等价于 x = x & 1
  8. |= x |= 1 等价于 x = x | 1
  9. ^= x ^= 1 等价于 x = x ^ 1
  10. <<= x <<= 1 等价于 x = x << 1
  11. >>= x >>= 1 等价于 x = x >> 1

记住二元运算符的等价形式,它们的含义都不难理解。

其他运算符

最后,我们来说几个不好归类的其他几个运算符。

  1. condition ? a : b 条件表达式,condition 条件为真,返回 a,否则返回 b。
  2. sizeof(类型) 返回类型对象的字节数 。
  3. , 逗号运算符,返回逗号列表最后一个表达式的值
  4. cast 强制类型转换
  5. & 取址运算符,返回变量的地址
  6. * 寻址运算符,返回地址中的值

虽然都是一些杂项运算符,但他们的用法和作用都并不简单,我们最后用一份代码作为示例,结束今天的课程:

#include <iostream>

using namespace std;

int main() {

int x = 1, y = 2, z;


// 1. condition ? a : b 条件表达式,condition 条件为真,返回 a,否则返回 b。
z = (x > y) ? 3 : 4;
cout << "(x > y) ? 3 : 4 = " << z << endl;

// 2. sizeof(类型) 返回类型对象的字节数 。
cout << "int 类型的字节数:" << sizeof(x) << endl;

// 3. , 逗号运算符,返回逗号列表最后一个表达式的值
z = (x++, y = x + 1, ++y);
cout << "(x++, y = x+1, ++y) = " << z << endl; // 4

// 4. cast 强制类型转换
x = int(1.23);
cout << "int(1.23) = " << x << endl;

// 5. & 取址运算符,返回变量的地址
int *p = &x;
cout << "&x = " << p << endl;

// 6. * 寻址运算符,返回地址中的值
cout << "*p = " << *p << endl;

return 0;
}

/*
output:
(x > y) ? 3 : 4 = 4
int 类型的字节数:4
(x++, y = x+1, ++y) = 4
int(1.23) = 1
&x = 0x7ffee6fa2e28
*p = 1
*/