基本运算
约 6765 字大约 23 分钟
2025-06-25
算术运算符
6种基础算术运算符
C++ 提供 6 种基本算术运算符,用于整数(int、long long等)和浮点数(float、double)的运算:
运算符 | 名称 | 示例(a=10, b=3) | 结果(整数) | 结果(浮点数) |
---|---|---|---|---|
+ | 加法 | a + b | 13 | 13.0 |
- | 减法 | a - b | 7 | 7.0 |
* | 乘法 | a * b | 30 | 30.0 |
/ | 除法(分情况) | a / b | 3(整除) | 3.333...(精确除) |
% | 求余(取模) | a % b | 1 | 不支持(编译错误) |
关键特性与信奥赛考点
考点
1. 除法(/):整数整除 vs 浮点数精确除
整数除法:当两个操作数都是整数时,/ 执行整除(向下取整,截断小数部分)。
int a = 10, b = 3;
cout << a / b; // 输出3(而非3.333)
cout << -10 / 3; // 输出-3(向下取整,而非-3.333→-3)
浮点数除法:只要有一个操作数是浮点数(float/double),/ 执行精确除法。
double a = 10.0, b = 3;
cout << a / b; // 输出3.333333...
信奥赛技巧:若需对整数做精确除法(保留小数),需先强制转换为浮点数:
int a = 10, b = 3;
cout << (double)a / b; // 输出3.333333...
2. 求余(%):仅支持整数,结果符号与被除数一致
定义:a % b
表示 “a除以b的余数”,数学上满足 a = (a / b) * b + (a % b)
。 限制:% 的两个操作数必须都是整数,若用浮点数会编译错误。 符号规则:余数的符号与被除数(左操作数) 一致(与 C++ 标准一致)。
cout << 10 % 3; // 1(10 = 3*3 + 1)
cout << -10 % 3; // -1(-10 = (-3)*3 + (-1))
cout << 10 % -3; // 1(10 = (-3)*(-3) + 1)
信奥赛应用:
- 判断奇偶性:
n % 2 == 0
为偶数,n % 2 == 1
为奇数(注意负数:-3 % 2 = -1
,需用n % 2 != 0
判断奇数)。 取最后k
位数字:n % 10
取个位,n % 100
取后两位等。 - 周期性问题:如 “每隔 k 步执行操作”(
i % k == 0
)。
3. 运算优先级与结合性
- 优先级:与数学一致,
*、/、%
优先级高于+、-
;同优先级从左到右计算。 例:a + b * c
等价于a + (b * c)
;a / b * c
等价于(a / b) * c
。 - 括号优先:复杂表达式需用 () 强制改变优先级,避免歧义。 例:
(a + b) * c
(先加后乘)。
4. 数据类型与溢出问题
- 整数溢出:当运算结果超过当前数据类型的范围时,会产生未定义行为(信奥赛高频坑点)。 例:
int
范围约为-2^31 ~ 2^31-1(±21 亿)
,若计算1e9 * 1e9
会溢出。 - 解决方案:使用更大的类型(如
long long
,范围±9e18
)。
long long a = 1e9, b = 1e9;
cout << a * b; // 正确输出1000000000000000000
信奥赛注意:初始化时需加后缀 LL 表示 long long,避免整数常量溢出:
long long x = 1e18; // 错误:1e18是double,可能丢失精度
long long x = 1000000000000000000LL; // 正确
常见错误与避坑指南
常见错误
- 整数除法丢失精度: 错误:
int a = 5, b = 2
;double c = a / b;
(c 为 2.0,而非 2.5)。 正确:double c = (double)a / b;
(强制转换后除法)。 求余操作数为 0: 若b = 0
,a / b
或a % b
会导致程序崩溃(除以零错误),信奥赛中需提前判断b != 0
。 - 符号错误: 负数求余时,结果符号与被除数一致,需注意逻辑判断(如判断奇数不能仅用
n % 2 == 1
,需用n % 2 != 0
)。 - 溢出未处理: 乘法 / 加法运算前预估结果范围,超过 int 则用 long long,避免测试点因溢出 WA( Wrong Answer)。
信奥赛实战示例
实战
题目:输入两个整数 a 和 b,输出它们的和、差、积、商(整除)、余数。
#include <iostream>
using namespace std;
int main() {
int a, b;
cin >> a >> b;
cout << a + b << endl;
cout << a - b << endl;
cout << (long long)a * b << endl; // 防止乘法溢出
cout << a / b << endl; // 整除
cout << a % b << endl; // 余数
return 0;
}
关系运算符
6种关系运算符
在C++中,关系运算用于比较两个值的大小或相等关系,返回结果为布尔值(true
或false
),是信奥赛中条件判断(如if
、while
)的核心基础。
一、关系运算符列表
C++提供6种关系运算符,用于比较整数、浮点数等基本类型:
运算符 | 名称 | 含义 | 示例(a=5, b=3) | 结果 |
---|---|---|---|---|
> | 大于 | 左操作数 > 右操作数 | a > b | true (1) |
>= | 大于等于 | 左操作数 ≥ 右操作数 | a >= b | true (1) |
< | 小于 | 左操作数 < 右操作数 | a < b | false (0) |
<= | 小于等于 | 左操作数 ≤ 右操作数 | a <= b | false (0) |
== | 等于 | 左操作数 = 右操作数 | a == b | false (0) |
!= | 不等于 | 左操作数 ≠ 右操作数 | a != b | true (1) |
二、关键特性与信奥赛考点
考点
1. 运算结果:布尔值的表示
- 关系运算的结果是布尔类型(
bool
),在C++中:- 成立时返回
true
(实际存储为整数1
); - 不成立时返回
false
(实际存储为整数0
)。
- 成立时返回
- 示例:
bool res1 = 5 > 3; // res1 = true(1)
bool res2 = 5 == 3; // res2 = false(0)
cout << res1; // 输出1
cout << res2; // 输出0
2. 易错点:==
与 =
的区别
==
:关系运算符,用于判断“相等”,返回true
或false
。=
:赋值运算符,用于给变量赋值,返回赋值后的结果。- 信奥赛高频错误:在条件判断中误用
=
代替==
。
错误示例:正确示例:c++int x = 5; if (x = 3) { // 错误:此处是赋值(x变为3),而非判断相等 // 该代码块会执行(因为3是非0值,被视为true) }
c++if (x == 3) { // 正确:判断x是否等于3 // 只有x为3时才执行 }
3. 比较规则与数据类型
- 整数比较:直接按数值大小比较(支持负数,如
-5 < 3
为true
)。 - 浮点数比较:
- 由于精度误差(如
0.1
无法精确存储),禁止直接用==
或!=
比较浮点数。 - 信奥赛正确做法:判断两数之差的绝对值是否小于一个极小值(如
1e-9
)。c++double a = 0.1 + 0.2; double b = 0.3; // 错误:直接比较可能返回false(因精度误差) if (a == b) { ... } // 正确:判断差值小于极小值 const double eps = 1e-9; // 精度阈值 if (abs(a - b) < eps) { // 认为a和b相等 cout << "近似相等"; }
- 由于精度误差(如
- 不同类型比较:会发生隐式类型转换(如
int
转为double
),但不建议混合类型比较,易出错。
4. 优先级与结合性
- 优先级:关系运算符优先级低于算术运算符,高于逻辑运算符(
&&
、||
)。
例:a + b > c * d
等价于(a + b) > (c * d)
(先算算术运算,再比较)。 - 结合性:关系运算符从左到右结合,但连续比较需注意逻辑正确性。
错误示例:判断x
是否在[1, 10]
之间:正确示例:c++if (1 <= x <= 10) { ... } // 错误!等价于(1 <= x) <= 10,结果恒为true
c++if (x >= 1 && x <= 10) { ... } // 需用逻辑与(&&)连接
三、信奥赛实战应用
实战应用
- 条件判断(
if
语句):
int score;
cin >> score;
if (score >= 60) { // 判断分数是否及格
cout << "及格";
} else {
cout << "不及格";
}
- 循环控制(
while
/for
):
int i = 1;
while (i <= 100) { // 当i小于等于100时循环
cout << i << " ";
i++;
}
- 数组元素筛选:
int arr[] = {3, 7, 2, 9, 5};
for (int i = 0; i < 5; i++) {
if (arr[i] % 2 != 0) { // 筛选奇数
cout << arr[i] << " ";
}
}
四、避坑指南
避坑指南
- 区分
==
(比较)和=
(赋值),尤其在if
、while
条件中。 - 浮点数比较不用
==
/!=
,而用“差值小于精度阈值”判断。 - 连续范围判断(如
a < x < b
)需拆分为x > a && x < b
。 - 比较有符号与无符号整数时会触发类型转换,可能导致逻辑错误(如
-1 > (unsigned int)1
结果为true
)。
逻辑运算符
逻辑运算符
在C++中,逻辑运算用于组合多个条件判断,返回布尔值(true
或false
),是信奥赛中实现复杂逻辑控制(如多条件判断、循环终止条件)的核心工具。
一、逻辑运算符列表
C++提供3种基本逻辑运算符,用于连接关系表达式或布尔值:
运算符 | 名称 | 含义 | 运算规则(a 和b 为布尔值) |
---|---|---|---|
&& | 逻辑与 | 两侧条件同时成立才为真 | a && b 为 true 当且仅当 a 和 b 都为 true ,否则为 false |
` | ` | 逻辑或 | |
! | 逻辑非 | 取反操作 | !a 为 true 当且仅当 a 为 false ;!a 为 false 当且仅当 a 为 true |
二、关键特性与信奥赛考点
考点
1. 运算结果:布尔值的表示
- 逻辑运算的结果是布尔类型(
bool
):- 真(成立)用
true
表示(存储为整数1
); - 假(不成立)用
false
表示(存储为整数0
)。
- 真(成立)用
- 示例:
bool res1 = (5 > 3) && (2 < 1); // false(第二个条件不成立)
bool res2 = (5 > 3) || (2 < 1); // true(第一个条件成立)
bool res3 = !(5 > 3); // false(对true取反)
cout << res1 << " " << res2 << " " << res3; // 输出:0 1 0
2. 短路求值(信奥赛高频考点)
C++逻辑运算具有短路特性:一旦能确定整个表达式的结果,就不再计算后续部分,这是优化效率和避免错误的关键。
&&
的短路:若左侧条件为false
,则右侧条件不再计算(因为整体已确定为false
)。
例:c++int a = 1, b = 1; bool res = (a == 0) && (++b); // 左侧a==0为false,右侧++b不执行 cout << b; // 输出1(b未被自增)
||
的短路:若左侧条件为true
,则右侧条件不再计算(因为整体已确定为true
)。
例:c++int a = 1, b = 1; bool res = (a == 1) || (++b); // 左侧a==1为true,右侧++b不执行 cout << b; // 输出1(b未被自增)
信奥赛应用:利用短路避免无效计算或错误(如除以零):
c++int x, y; cin >> x >> y; // 先判断y!=0,避免y=0时执行x/y导致崩溃 if (y != 0 && x / y > 2) { cout << "满足条件"; }
3. 运算符优先级与结合性
优先级:
- 逻辑非(
!
)优先级最高(高于算术、关系运算符); - 逻辑与(
&&
)次之; - 逻辑或(
||
)优先级最低(低于关系运算符)。
例:
c++// 等价于:( (a > b) && (c < d) ) || ( !e ) bool res = a > b && c < d || !e;
- 逻辑非(
结合性:
!
右结合(从右到左);&&
和||
左结合(从左到右)。
复杂表达式建议用()
明确优先级,避免歧义:
c++// 不推荐:依赖优先级,可读性差 if (a || b && c) { ... } // 推荐:用括号明确逻辑 if (a || (b && c)) { ... } // 先算b&&c,再和a做||
4. 非布尔值的逻辑判断
C++中,非布尔值(如整数、指针)也可参与逻辑运算:
- 0 被视为
false
,非0值被视为true
。 - 示例:
int x = 5, y = 0;
cout << (x && y); // 0(y是0,视为false,整体为false)
cout << (x || y); // 1(x是非0,视为true,整体为true)
cout << !x; // 0(x是非0,!x为false)
- 信奥赛应用:判断变量是否为0(无需显式写
x == 0
):
int n;
cin >> n;
if (!n) { // 等价于 if (n == 0)
cout << "n是0";
}
三、信奥赛实战应用
实战
多条件筛选:
题目:判断一个数是否是“大于10的偶数”c++int num; cin >> num; if (num > 10 && num % 2 == 0) { // 同时满足两个条件 cout << "是大于10的偶数"; }
范围判断:
题目:判断分数是否在[0, 100]
之间c++int score; cin >> score; if (score < 0 || score > 100) { // 超出范围(两个条件满足一个即可) cout << "分数无效"; } else { cout << "分数有效"; }
逻辑取反:
题目:判断一个数不是3的倍数c++int n; cin >> n; if (!(n % 3 == 0)) { // 对“是3的倍数”取反 cout << "不是3的倍数"; }
四、避坑指南
注意
区分逻辑与(
&&
)和按位与(&
)、逻辑或(||
)和按位或(|
):&&
/||
用于逻辑判断,返回bool
;&
/|
用于二进制位运算,操作数逐位计算(信奥赛中常因手误混淆)。
短路求值的副作用:
若右侧表达式包含修改变量的操作(如++
、--
),短路可能导致预期外的结果,需谨慎使用。复杂逻辑拆分:
多个逻辑运算符嵌套时,用括号或临时变量拆分,提高可读性(如bool cond = (a > b) && (c < d);
)。
变量自增与自减运算
自增自减
在C++中,自增(++
)和自减(--
)是专门用于变量增减1的运算符,在信奥赛中频繁用于循环计数、索引移动等场景。
一、基本用法
自增/自减运算符分为两种形式,作用都是将变量的值加1或减1,但返回值不同:
运算符 | 名称 | 作用 | 示例(int a = 5 ) | 执行后a 的值 | 表达式返回值 |
---|---|---|---|---|---|
++a | 前置自增 | 先加1,再返回新值 | ++a | 6 | 6 |
a++ | 后置自增 | 先返回原值,再加1 | a++ | 6 | 5 |
--a | 前置自减 | 先减1,再返回新值 | --a | 4 | 4 |
a-- | 后置自减 | 先返回原值,再减1 | a-- | 4 | 5 |
二、核心区别:前置与后置
核心区别
1. 运算顺序差异(信奥赛核心考点)
前置形式(
++a
/--a
):
先对变量执行增减操作,再将新值作为表达式的结果。
示例:c++int a = 5, b; b = ++a; // 步骤1:a变为6;步骤2:b赋值为6 cout << a << " " << b; // 输出:6 6
后置形式(
a++
/--a
):
先将变量的原值作为表达式的结果,再对变量执行增减操作。
示例:c++int a = 5, b; b = a++; // 步骤1:b赋值为5;步骤2:a变为6 cout << a << " " << b; // 输出:6 5
2. 单独使用时的等价性
当自增/自减运算符单独作为一条语句(不参与其他表达式)时,前置与后置效果相同,仅影响变量自身:
int a = 5;
a++; // 等价于 ++a; 执行后a都为6
信奥赛中,for
循环的计数器通常用后置自增(i++
),但用前置(++i
)也完全正确:
for (int i = 0; i < 5; i++) { ... } // 常用写法
for (int i = 0; i < 5; ++i) { ... } // 效果相同,效率略高(可忽略)
三、信奥赛常见应用
应用
循环控制:
用自增运算符实现循环变量的递增,是for
循环的标准写法:c++// 输出1~10的整数 for (int i = 1; i <= 10; i++) { cout << i << " "; }
数组索引操作:
遍历数组时,用自增运算符移动索引:c++int arr[] = {10, 20, 30, 40}; int i = 0; while (i < 4) { cout << arr[i++] << " "; // 先访问arr[i],再i+1 }
简化赋值语句:
替代a = a + 1
或a += 1
,使代码更简洁:c++int count = 0; count++; // 等价于 count = count + 1; 或 count += 1;
四、易错点与避坑指南
注意
1. 在同一表达式中多次操作同一变量
C++标准未定义“同一表达式中对同一变量多次自增/自减”的行为,不同编译器可能产生不同结果,信奥赛中绝对禁止此类写法:
int a = 5;
// 错误示例:未定义行为,结果不可预测
cout << a++ + ++a; // 不同编译器可能输出11、12或其他值
2. 混淆前置/后置导致逻辑错误
在条件判断或赋值中,误用前置/后置会改变程序逻辑:
int a = 5;
// 需求:判断a是否等于5,然后a加1
if (a++ == 5) { // 正确:先判断a=5(成立),再a变为6
cout << "条件成立"; // 会执行
}
int b = 5;
if (++b == 5) { // 错误:先b变为6,再判断6==5(不成立)
cout << "条件成立"; // 不会执行
}
3. 对常量或表达式使用自增/自减
自增/自减运算符的操作数必须是可修改的变量(左值),不能用于常量或表达式:
5++; // 错误:常量不能自增
(a + b)++; // 错误:表达式结果是右值,不能修改
4. 与其他运算符结合时的优先级问题
自增/自减的优先级高于算术运算符和关系运算符,低于括号:
int a = 3;
cout << a++ * 2; // 等价于 (a++) * 2 → 3*2=6(执行后a=4)
cout << (++a) * 2; // 等价于 (++a)*2 → 5*2=10(执行后a=5)
五、信奥赛实战示例
实战
题目:输入一个整数n
,输出1到n
的累加和(用自增实现)。
#include <iostream>
using namespace std;
int main() {
int n, sum = 0;
cin >> n;
int i = 1;
while (i <= n) {
sum += i++; // 先加i的当前值,再加1(等价于sum += i; i++;)
}
cout << sum << endl;
return 0;
}
三目运算
三目运算符
在C++中,三目运算符(?:
)是一种简洁的条件判断语法,可替代简单的if-else
语句,在信奥赛中常用于简化赋值或返回值逻辑。它是C++中唯一需要三个操作数的运算符,使用灵活但需注意可读性。
一、基本语法与作用
基本语法
三目运算符的语法格式如下:
条件表达式 ? 表达式1 : 表达式2;
运算规则:
- 先判断“条件表达式”的真假(
true
或false
)。 - 若条件为
true
,则整个表达式的结果为“表达式1”的值。 - 若条件为
false
,则整个表达式的结果为“表达式2”的值。
示例:
int a = 5, b = 3;
int max_val = (a > b) ? a : b; // 条件a>b为true,结果为a的值5
cout << max_val; // 输出5
二、核心特性与信奥赛考点
考点
1. 与if-else
的等价性
简单的if-else
逻辑可直接用三目运算符替换,代码更简洁:
// if-else写法
int max_val;
if (a > b) {
max_val = a;
} else {
max_val = b;
}
// 三目运算符写法(等价)
int max_val = (a > b) ? a : b;
信奥赛中,三目运算符特别适合单行条件赋值,减少代码行数。
2. 表达式的返回值类型
三目运算符的结果类型由“表达式1”和“表达式2”共同决定:
- 若两者类型相同,结果为该类型。
- 若类型不同,C++会尝试自动转换为兼容类型(如
int
与double
转换为double
)。
示例:
int a = 5;
double b = 3.5;
double res = (a > 0) ? a : b; // int转换为double,结果为5.0
3. 嵌套使用(谨慎!)
三目运算符支持嵌套,用于多条件判断,但嵌套过多会降低可读性:
// 判断x的正负性
int x = -3;
string res = (x > 0) ? "正数" : (x == 0) ? "零" : "负数";
cout << res; // 输出"负数"
信奥赛建议:嵌套不超过2层,复杂逻辑优先用if-else
。
4. 优先级与结合性
- 优先级:低于关系运算符和算术运算符,高于赋值运算符。
例:a + b > c ? x : y
等价于(a + b > c) ? x : y
。 - 结合性:右结合(从右到左),但建议用括号明确逻辑:
例:a ? b : c ? d : e
等价于a ? b : (c ? d : e)
(而非(a ? b : c) ? d : e
)。
三、信奥赛实战应用
实战
简化条件赋值:
题目:求两个数的较小值c++int a, b; cin >> a >> b; int min_val = (a < b) ? a : b; // 一行代码实现 cout << min_val;
在输出语句中直接使用:
题目:判断一个数是否为偶数并输出对应文字c++int n; cin >> n; cout << (n % 2 == 0 ? "偶数" : "奇数"); // 直接嵌入输出语句
作为函数返回值:
题目:编写函数返回两个数的最大值c++int max(int x, int y) { return (x > y) ? x : y; // 简化返回逻辑 }
四、避坑指南
注意事项
避免复杂表达式:
若“表达式1”或“表达式2”包含多条语句(如自增、赋值),会降低可读性,建议用if-else
:c++// 不推荐:逻辑复杂 int a = 5, b = 3; int res = (a > b) ? (a++, a) : (b++, b); // 推荐:清晰明了 int res; if (a > b) { a++; res = a; } else { b++; res = b; }
注意类型转换问题:
当“表达式1”和“表达式2”类型不同时,可能导致精度丢失(如double
转int
):c++int res = (5 > 3) ? 10.5 : 3; // 10.5转为int,结果为10(丢失小数)
括号的使用:
在复杂表达式中,用括号明确优先级,避免歧义:c++// 易混淆:等价于 (a > b) ? (a + c) : b int res = a > b ? a + c : b; // 清晰:用括号明确逻辑 int res = (a > b) ? (a + c) : b;
三目运算符是信奥赛中简化代码的实用工具,尤其适合简单条件判断场景。但需平衡简洁性与可读性,避免过度嵌套或使用复杂表达式,这是写出高效且易维护代码的关键。
位运算
位运算
在C++中,位运算直接对整数的二进制位进行操作,是信奥赛中优化代码效率、处理二进制问题的重要工具。
一、位运算符列表
位运算针对整数的二进制位(0或1)进行操作,常用运算符如下:
运算符 | 名称 | 作用说明(以二进制位为单位) | 示例(a=6即0110,b=3即0011) | 结果(二进制) | 结果(十进制) |
---|---|---|---|---|---|
& | 按位与 | 对应位都为1则结果为1,否则为0 | a & b | 0010 | 2 |
或 | 按位或 | 对应位至少有一个为1则结果为1,否则为0 | a 或 b | 0111 | 7 |
~ | 按位非 | 对每个位取反(0变1,1变0),符号位也会取反 | ~a | 1001(假设4位) | -7(补码规则) |
^ | 按位异或 | 对应位不同则结果为1,相同则为0 | a ^ b | 0101 | 5 |
<< | 左移 | 将二进制位整体左移n位,右侧补0 | a << 1 | 1100 | 12 |
>> | 右移 | 将二进制位整体右移n位,左侧补符号位(正数补0,负数补1) | a >> 1 | 0011 | 3 |
二、核心特性与信奥赛考点
考点
1. 按位与(&
)
- 作用:保留二进制中“都为1”的位,可用于清0特定位或判断奇偶性。
- 信奥赛应用:
- 判断奇偶性:
n & 1
结果为1则n是奇数,为0则是偶数(比n % 2
效率高)。
c++int n = 5; // 二进制101 cout << (n & 1); // 1(奇数)
- 清0低位:
n & (~((1 << k) - 1))
可将n的低k位清0(如k=2时,n & 11111100
)。
- 判断奇偶性:
2. 按位或(|
)
- 作用:将二进制中“至少有一个1”的位设为1,可用于置1特定位。
- 信奥赛应用:
- 置1特定位:
n | (1 << k)
可将n的第k位(从0开始)设为1。
c++int n = 4; // 二进制100 n |= (1 << 1); // 第1位置1 → 110(6) cout << n; // 输出6
- 置1特定位:
3. 按位非(~
)
- 作用:对所有位取反(包括符号位),结果与原数满足
~n = -n - 1
(补码特性)。 - 注意:C++中整数默认是有符号类型,取反后符号位变化,可能导致负数。
示例:c++int n = 6; // 二进制000...0110(32位) cout << ~n; // 结果为111...1001 → 十进制-7
4. 按位异或(^
)
- 作用:对应位不同则为1,可用于翻转特定位、交换变量或找唯一出现奇数次的数。
- 信奥赛核心应用:
- 翻转特定位:
n ^ (1 << k)
可将n的第k位翻转(0变1,1变0)。
c++int n = 6; // 0110 n ^= (1 << 2); // 翻转第2位 → 0010(2)
- 交换变量(无需临时变量):
c++int a = 3, b = 5; a ^= b; // a = 3^5 b ^= a; // b = 5^(3^5) = 3 a ^= b; // a = (3^5)^3 = 5 // 最终a=5,b=3
- 找唯一奇数次元素:数组中只有一个数出现奇数次,其余出现偶数次,异或所有元素结果即为该数。
- 翻转特定位:
5. 左移(<<
)
- 作用:将二进制位左移n位,等价于
n * (2^k)
(无溢出时),效率远高于乘法。 - 示例:
5 << 2
即5×2²=20(二进制101 → 10100)。 - 信奥赛应用:快速计算2的幂(
1 << k
即2ᵏ)。
6. 右移(>>
)
- 作用:将二进制位右移n位,正数等价于
n / (2^k)
(向下取整),负数因符号位补1,结果可能与预期不同。 - 示例:
int a = 8; // 1000
cout << (a >> 2); // 10(2),等价于8/4=2
int b = -8; // 补码111...1000
cout << (b >> 2); // -2(不同编译器可能有差异)
三、位运算优先级与结合性
位运算优先级与结合性
- 优先级:
~
最高,其次是<<
和>>
,然后是&
,接着是^
,最后是|
。
例:a & b << c
等价于a & (b << c)
。 - 结合性:除
~
右结合外,其余均左结合。 - 建议:复杂表达式用括号明确顺序,避免歧义。
四、信奥赛实战应用
实战
判断一个数是否为2的幂:
2的幂的二进制只有一位是1,如8(1000),n & (n-1) == 0
即可判断:c++bool isPowerOfTwo(int n) { return n > 0 && (n & (n - 1)) == 0; }
计算二进制中1的个数:
每次用n & (n-1)
清除最后一位1,计数直到n为0:c++int countOne(int n) { int cnt = 0; while (n) { n &= n - 1; cnt++; } return cnt; }
获取二进制的第k位:
用(n >> k) & 1
提取第k位(0或1):c++int getBit(int n, int k) { return (n >> k) & 1; }
五、避坑指南
注意事项
溢出问题:左移可能导致整数溢出(如
1 << 31
对32位int会溢出为负数),信奥赛中需用long long
避免:long long val = 1LL << 60; // 正确,用1LL确保类型为long long
负数右移:不同编译器对负数右移处理可能不同(补1或补0),信奥赛中尽量对非负数使用右移。
位运算与逻辑运算混淆:
&
/|
是位运算,&&
/||
是逻辑运算,不可混用(如if (n & 1)
不能写成if (n && 1)
)。位运算在信奥赛中是优化时间复杂度的关键(如将O(n)优化为O(log n)),尤其在处理二进制、状态压缩、位掩码等问题时不可替代。