Skip to content

基本运算

约 6765 字大约 23 分钟

2025-06-25

算术运算符

6种基础算术运算符

C++ 提供 6 种基本算术运算符,用于整数(int、long long等)和浮点数(float、double)的运算:

运算符名称示例(a=10, b=3)结果(整数)结果(浮点数)
+加法a + b1313.0
-减法a - b77.0
*乘法a * b3030.0
/除法(分情况)a / b3(整除)3.333...(精确除)
%求余(取模)a % b1不支持(编译错误)

关键特性与信奥赛考点

考点

1. 除法(/):整数整除 vs 浮点数精确除

整数除法:当两个操作数都是整数时,/ 执行整除(向下取整,截断小数部分)。

c++
int a = 10, b = 3;
cout << a / b;  // 输出3(而非3.333)
cout << -10 / 3; // 输出-3(向下取整,而非-3.333→-3)

浮点数除法:只要有一个操作数是浮点数(float/double),/ 执行精确除法。

c++
double a = 10.0, b = 3;
cout << a / b;  // 输出3.333333...

信奥赛技巧:若需对整数做精确除法(保留小数),需先强制转换为浮点数:

c++
int a = 10, b = 3;
cout << (double)a / b;  // 输出3.333333...

2. 求余(%):仅支持整数,结果符号与被除数一致

定义:a % b 表示 “a除以b的余数”,数学上满足 a = (a / b) * b + (a % b)。 限制:% 的两个操作数必须都是整数,若用浮点数会编译错误。 符号规则:余数的符号与被除数(左操作数) 一致(与 C++ 标准一致)。

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)

信奥赛应用

  1. 判断奇偶性:n % 2 == 0 为偶数,n % 2 == 1 为奇数(注意负数:-3 % 2 = -1,需用 n % 2 != 0 判断奇数)。 取最后 k 位数字:n % 10 取个位,n % 100 取后两位等。
  2. 周期性问题:如 “每隔 k 步执行操作”(i % k == 0)。

3. 运算优先级与结合性

  1. 优先级:与数学一致,*、/、% 优先级高于 +、-;同优先级从左到右计算。 例:a + b * c 等价于 a + (b * c)a / b * c 等价于 (a / b) * c
  2. 括号优先:复杂表达式需用 () 强制改变优先级,避免歧义。 例:(a + b) * c(先加后乘)。

4. 数据类型与溢出问题

  1. 整数溢出:当运算结果超过当前数据类型的范围时,会产生未定义行为(信奥赛高频坑点)。 例:int 范围约为 -2^31 ~ 2^31-1(±21 亿),若计算 1e9 * 1e9 会溢出。
  2. 解决方案:使用更大的类型(如 long long,范围 ±9e18)。
c++
long long a = 1e9, b = 1e9;
cout << a * b;  // 正确输出1000000000000000000

信奥赛注意:初始化时需加后缀 LL 表示 long long,避免整数常量溢出:

c++
long long x = 1e18;       // 错误:1e18是double,可能丢失精度
long long x = 1000000000000000000LL;  // 正确

常见错误与避坑指南

常见错误

  1. 整数除法丢失精度: 错误:int a = 5, b = 2; double c = a / b;(c 为 2.0,而非 2.5)。 正确:double c = (double)a / b;(强制转换后除法)。 求余操作数为 0: 若 b = 0a / ba % b 会导致程序崩溃(除以零错误),信奥赛中需提前判断 b != 0
  2. 符号错误: 负数求余时,结果符号与被除数一致,需注意逻辑判断(如判断奇数不能仅用 n % 2 == 1,需用 n % 2 != 0)。
  3. 溢出未处理: 乘法 / 加法运算前预估结果范围,超过 int 则用 long long,避免测试点因溢出 WA( Wrong Answer)。

信奥赛实战示例

实战

题目:输入两个整数 a 和 b,输出它们的和、差、积、商(整除)、余数。

c++
#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++中,关系运算用于比较两个值的大小或相等关系,返回结果为布尔值(truefalse),是信奥赛中条件判断(如ifwhile)的核心基础。

一、关系运算符列表

C++提供6种关系运算符,用于比较整数、浮点数等基本类型:

运算符名称含义示例(a=5, b=3)结果
>大于左操作数 > 右操作数a > btrue(1)
>=大于等于左操作数 ≥ 右操作数a >= btrue(1)
<小于左操作数 < 右操作数a < bfalse(0)
<=小于等于左操作数 ≤ 右操作数a <= bfalse(0)
==等于左操作数 = 右操作数a == bfalse(0)
!=不等于左操作数 ≠ 右操作数a != btrue(1)

二、关键特性与信奥赛考点

考点

1. 运算结果:布尔值的表示

  • 关系运算的结果是布尔类型(bool,在C++中:
    • 成立时返回 true(实际存储为整数1);
    • 不成立时返回 false(实际存储为整数0)。
  • 示例:
c++
bool res1 = 5 > 3;  // res1 = true(1)
bool res2 = 5 == 3; // res2 = false(0)
cout << res1;  // 输出1
cout << res2;  // 输出0

2. 易错点:=== 的区别

  • ==:关系运算符,用于判断“相等”,返回truefalse
  • =:赋值运算符,用于给变量赋值,返回赋值后的结果。
  • 信奥赛高频错误:在条件判断中误用=代替==
    错误示例:
    c++
    int x = 5;
    if (x = 3) {  // 错误:此处是赋值(x变为3),而非判断相等
        // 该代码块会执行(因为3是非0值,被视为true)
    }
    正确示例:
    c++
    if (x == 3) {  // 正确:判断x是否等于3
        // 只有x为3时才执行
    }

3. 比较规则与数据类型

  • 整数比较:直接按数值大小比较(支持负数,如 -5 < 3true)。
  • 浮点数比较
    • 由于精度误差(如 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) { ... }  // 需用逻辑与(&&)连接

三、信奥赛实战应用

实战应用

  1. 条件判断(if语句)
c++
int score;
cin >> score;
if (score >= 60) {  // 判断分数是否及格
    cout << "及格";
} else {
    cout << "不及格";
}
  1. 循环控制(while/for
c++
int i = 1;
while (i <= 100) {  // 当i小于等于100时循环
    cout << i << " ";
    i++;
}
  1. 数组元素筛选
c++
int arr[] = {3, 7, 2, 9, 5};
for (int i = 0; i < 5; i++) {
    if (arr[i] % 2 != 0) {  // 筛选奇数
        cout << arr[i] << " ";
    }
}

四、避坑指南

避坑指南

  1. 区分==(比较)和=(赋值),尤其在ifwhile条件中。
  2. 浮点数比较不用==/!=,而用“差值小于精度阈值”判断。
  3. 连续范围判断(如a < x < b)需拆分为x > a && x < b
  4. 比较有符号与无符号整数时会触发类型转换,可能导致逻辑错误(如-1 > (unsigned int)1 结果为true)。

逻辑运算符

逻辑运算符

在C++中,逻辑运算用于组合多个条件判断,返回布尔值(truefalse),是信奥赛中实现复杂逻辑控制(如多条件判断、循环终止条件)的核心工具。

一、逻辑运算符列表

C++提供3种基本逻辑运算符,用于连接关系表达式或布尔值:

运算符名称含义运算规则(ab为布尔值)
&&逻辑与两侧条件同时成立才为真a && btrue 当且仅当 ab 都为 true,否则为 false
``逻辑或
!逻辑非取反操作!atrue 当且仅当 afalse!afalse 当且仅当 atrue

二、关键特性与信奥赛考点

考点

1. 运算结果:布尔值的表示

  • 逻辑运算的结果是布尔类型(bool
    • 真(成立)用 true 表示(存储为整数1);
    • 假(不成立)用 false 表示(存储为整数0)。
  • 示例:
c++
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
  • 示例:
c++
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):
c++
int n;
cin >> n;
if (!n) {  // 等价于 if (n == 0)
    cout << "n是0";
}

三、信奥赛实战应用

实战

  1. 多条件筛选
    题目:判断一个数是否是“大于10的偶数”

    c++
    int num;
    cin >> num;
    if (num > 10 && num % 2 == 0) {  // 同时满足两个条件
        cout << "是大于10的偶数";
    }
  2. 范围判断
    题目:判断分数是否在 [0, 100] 之间

    c++
    int score;
    cin >> score;
    if (score < 0 || score > 100) {  // 超出范围(两个条件满足一个即可)
        cout << "分数无效";
    } else {
        cout << "分数有效";
    }
  3. 逻辑取反
    题目:判断一个数不是3的倍数

    c++
    int n;
    cin >> n;
    if (!(n % 3 == 0)) {  // 对“是3的倍数”取反
        cout << "不是3的倍数";
    }

四、避坑指南

注意

  1. 区分逻辑与(&&)和按位与(&)、逻辑或(||)和按位或(|):

    • &&/|| 用于逻辑判断,返回 bool
    • &/| 用于二进制位运算,操作数逐位计算(信奥赛中常因手误混淆)。
  2. 短路求值的副作用:
    若右侧表达式包含修改变量的操作(如 ++--),短路可能导致预期外的结果,需谨慎使用。

  3. 复杂逻辑拆分:
    多个逻辑运算符嵌套时,用括号或临时变量拆分,提高可读性(如 bool cond = (a > b) && (c < d);)。

变量自增与自减运算

自增自减

在C++中,自增(++)和自减(--)是专门用于变量增减1的运算符,在信奥赛中频繁用于循环计数、索引移动等场景。

一、基本用法

自增/自减运算符分为两种形式,作用都是将变量的值加1或减1,但返回值不同:

运算符名称作用示例(int a = 5执行后a的值表达式返回值
++a前置自增先加1,再返回新值++a66
a++后置自增先返回原值,再加1a++65
--a前置自减先减1,再返回新值--a44
a--后置自减先返回原值,再减1a--45

二、核心区别:前置与后置

核心区别

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. 单独使用时的等价性

当自增/自减运算符单独作为一条语句(不参与其他表达式)时,前置与后置效果相同,仅影响变量自身:

c++
int a = 5;
a++;  // 等价于 ++a; 执行后a都为6

信奥赛中,for循环的计数器通常用后置自增(i++),但用前置(++i)也完全正确:

c++
for (int i = 0; i < 5; i++) { ... }  // 常用写法
for (int i = 0; i < 5; ++i) { ... }  // 效果相同,效率略高(可忽略)

三、信奥赛常见应用

应用

  1. 循环控制
    用自增运算符实现循环变量的递增,是for循环的标准写法:

    c++
    // 输出1~10的整数
    for (int i = 1; i <= 10; i++) {
        cout << i << " ";
    }
  2. 数组索引操作
    遍历数组时,用自增运算符移动索引:

    c++
    int arr[] = {10, 20, 30, 40};
    int i = 0;
    while (i < 4) {
        cout << arr[i++] << " ";  // 先访问arr[i],再i+1
    }
  3. 简化赋值语句
    替代a = a + 1a += 1,使代码更简洁:

    c++
    int count = 0;
    count++;  // 等价于 count = count + 1; 或 count += 1;

四、易错点与避坑指南

注意

1. 在同一表达式中多次操作同一变量

C++标准未定义“同一表达式中对同一变量多次自增/自减”的行为,不同编译器可能产生不同结果,信奥赛中绝对禁止此类写法:

c++
int a = 5;
// 错误示例:未定义行为,结果不可预测
cout << a++ + ++a;  // 不同编译器可能输出11、12或其他值

2. 混淆前置/后置导致逻辑错误

在条件判断或赋值中,误用前置/后置会改变程序逻辑:

c++
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. 对常量或表达式使用自增/自减

自增/自减运算符的操作数必须是可修改的变量(左值),不能用于常量或表达式:

c++
5++;        // 错误:常量不能自增
(a + b)++;  // 错误:表达式结果是右值,不能修改

4. 与其他运算符结合时的优先级问题

自增/自减的优先级高于算术运算符和关系运算符,低于括号:

c++
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的累加和(用自增实现)。

c++

三目运算

三目运算符

在C++中,三目运算符(?:)是一种简洁的条件判断语法,可替代简单的if-else语句,在信奥赛中常用于简化赋值或返回值逻辑。它是C++中唯一需要三个操作数的运算符,使用灵活但需注意可读性。

一、基本语法与作用

基本语法

三目运算符的语法格式如下:

c++
条件表达式 ? 表达式1 : 表达式2;

运算规则

  1. 先判断“条件表达式”的真假(truefalse)。
  2. 若条件为true,则整个表达式的结果为“表达式1”的值。
  3. 若条件为false,则整个表达式的结果为“表达式2”的值。

示例

c++
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逻辑可直接用三目运算符替换,代码更简洁:

c++
// 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++会尝试自动转换为兼容类型(如intdouble转换为double)。

示例

c++
int a = 5;
double b = 3.5;
double res = (a > 0) ? a : b;  // int转换为double,结果为5.0

3. 嵌套使用(谨慎!)

三目运算符支持嵌套,用于多条件判断,但嵌套过多会降低可读性:

c++
// 判断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)。

三、信奥赛实战应用

实战

  1. 简化条件赋值
    题目:求两个数的较小值

    c++
    int a, b;
    cin >> a >> b;
    int min_val = (a < b) ? a : b;  // 一行代码实现
    cout << min_val;
  2. 在输出语句中直接使用
    题目:判断一个数是否为偶数并输出对应文字

    c++
    int n;
    cin >> n;
    cout << (n % 2 == 0 ? "偶数" : "奇数");  // 直接嵌入输出语句
  3. 作为函数返回值
    题目:编写函数返回两个数的最大值

    c++
    int max(int x, int y) {
        return (x > y) ? x : y;  // 简化返回逻辑
    }

四、避坑指南

注意事项

  1. 避免复杂表达式
    若“表达式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;
    }
  2. 注意类型转换问题
    当“表达式1”和“表达式2”类型不同时,可能导致精度丢失(如doubleint):

    c++
    int res = (5 > 3) ? 10.5 : 3;  // 10.5转为int,结果为10(丢失小数)
  3. 括号的使用
    在复杂表达式中,用括号明确优先级,避免歧义:

    c++
    // 易混淆:等价于 (a > b) ? (a + c) : b
    int res = a > b ? a + c : b;
    
    // 清晰:用括号明确逻辑
    int res = (a > b) ? (a + c) : b;
  4. 三目运算符是信奥赛中简化代码的实用工具,尤其适合简单条件判断场景。但需平衡简洁性与可读性,避免过度嵌套或使用复杂表达式,这是写出高效且易维护代码的关键。

位运算

位运算

在C++中,位运算直接对整数的二进制位进行操作,是信奥赛中优化代码效率、处理二进制问题的重要工具。

一、位运算符列表

位运算针对整数的二进制位(0或1)进行操作,常用运算符如下:

运算符名称作用说明(以二进制位为单位)示例(a=6即0110,b=3即0011)结果(二进制)结果(十进制)
&按位与对应位都为1则结果为1,否则为0a & b00102
按位或对应位至少有一个为1则结果为1,否则为0a 或 b01117
~按位非对每个位取反(0变1,1变0),符号位也会取反~a1001(假设4位)-7(补码规则)
^按位异或对应位不同则结果为1,相同则为0a ^ b01015
<<左移将二进制位整体左移n位,右侧补0a << 1110012
>>右移将二进制位整体右移n位,左侧补符号位(正数补0,负数补1)a >> 100113

二、核心特性与信奥赛考点

考点

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

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,结果可能与预期不同。
  • 示例:
c++
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)
  • 结合性:除 ~ 右结合外,其余均左结合。
  • 建议:复杂表达式用括号明确顺序,避免歧义。

四、信奥赛实战应用

实战

  1. 判断一个数是否为2的幂
    2的幂的二进制只有一位是1,如8(1000),n & (n-1) == 0 即可判断:

    c++
    bool isPowerOfTwo(int n) {
        return n > 0 && (n & (n - 1)) == 0;
    }
  2. 计算二进制中1的个数
    每次用 n & (n-1) 清除最后一位1,计数直到n为0:

    c++
    int countOne(int n) {
        int cnt = 0;
        while (n) {
            n &= n - 1;
            cnt++;
        }
        return cnt;
    }
  3. 获取二进制的第k位
    (n >> k) & 1 提取第k位(0或1):

    c++
    int getBit(int n, int k) {
        return (n >> k) & 1;
    }

五、避坑指南

注意事项

  1. 溢出问题:左移可能导致整数溢出(如 1 << 31 对32位int会溢出为负数),信奥赛中需用 long long 避免:

    long long val = 1LL << 60;  // 正确,用1LL确保类型为long long
  2. 负数右移:不同编译器对负数右移处理可能不同(补1或补0),信奥赛中尽量对非负数使用右移。

  3. 位运算与逻辑运算混淆&/| 是位运算,&&/|| 是逻辑运算,不可混用(如 if (n & 1) 不能写成 if (n && 1))。

  4. 位运算在信奥赛中是优化时间复杂度的关键(如将O(n)优化为O(log n)),尤其在处理二进制、状态压缩、位掩码等问题时不可替代。