2026年9月 GESP C++ 三级真题解析:位运算占了半壁江山

四季读书网 6 0

2026年9月 GESP C++ 三级真题解析:位运算占了半壁江山

三级这次的单选题,有 6 道直接考位运算,加上补码与进制转换,编码类题目接近 10 道。这是三级最近几年最"硬"的一张卷。


一、答案速查

题号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
单选
D
D
A
C
B
C
D
C
A
A
D
C
D
B
B

判断题官方未公布答案,以下为按题意推算的参考答案。


二、单选题

板块 1:数据编码与补码(第 1、4 题)

第 1 题 → D(选"不正确的")

"反码运算中,最高位进位要抛掉"——这是补码的规则,不是反码的

两者的区别必须记牢:

最高位进位怎么处理
反码
进位要循环加回到最低位(循环进位)
补码
进位直接丢弃

第 4 题 → C(选"错误的")

  • A ✔ 正数的原码 = 反码 = 补码;
  • B ✔ 负数的补码 = 对应正数原码取反(得反码)再 +1;
  • C ✘ 错误:8 位补码的范围是 −128 ~ +127(最小值是 −128,不是 −127);
  • D ✔ 求补码的快捷法:从右往左找到第一个 1,这个 1 及其右边不变,左边全部取反

对比记忆:8 位原码/反码是 −127 ~ +127(有 ±0),补码是 −128 ~ +127(没有 −0,多表示一个负数)。

板块 2:进制转换(第 2 题)→ D

选项 D 关于数制的书写/分解表述有误(十六进制数使用 0x 前缀,如 0x1A;而"通常写作…"的写法把底数与数值搞混了)。

进制这块必须做到条件反射:

二进制 1011  →  1×8 + 0×4 + 1×2 + 1×1 = 11八进制 17    →  1×8 + 7 = 15十六进制 2F  →  2×16 + 15 = 47

板块 3:位运算核心(第 5、6、7、11、12 题)

第 5 题 → B:x & (x - 1) 统计二进制中 1 的个数

int x = 2026;  int count = 0;while (x > 0) { count++; x = x & (x - 1); }

x & (x - 1) 的作用是消掉最低位的那个 1,循环几次就说明有几个 1。

2026 的二进制是 11111101010,共有8个 1 → 输出 8。

这是三级最高频技巧,一定要背下来:x & (x - 1) 消 1,x & (-x) 取最低位 1。

第 6 题 → C(选"错误的")

选项
判断
A 全部异或找唯一数
✔ 5^7^9^7^5 = 9,成对的异或抵消
B 左移溢出后不再符合乘法规律
✔ 正确
有符号整数左移溢出时结果确定
✘ 错误,有符号数左移溢出是未定义行为
D 末两位为 00 一定是 4 的倍数
✔ 二进制末两位权重是 2 + 1 = 3,末两位 00 表示 4 的倍数

第 7 题 → D:3 & 7 | 15 ^ ~11

优先级:~><</>>>&>^>|(与逻辑运算的 ! > && > || 完全对应)

~11            = -12          (按位取反,~n = -n - 1)15 ^ (-12)     = -5           (00001111 ^ 11110100 = 11111011)3 & 7          = 3            (011 & 111 = 011)3 | (-5)       = -5           (00000011 | 11111011 = 11111011)

结果−5

第 11 题 → D(选"错误的")

  • A ✔ 正整数 num >> 1 等价于 num / 2 向下取整;
  • B ✔ num | 0x0F 把低 4 位置 1,高位不变;
  • C ✔ num ^ num 一定等于 0;
  • D ✘ 错误:右移丢弃低位后,对负数并不符合数学除法规律(例如 −7 >> 1 = −4,而 −7 / 2 = −3)。

第 12 题 → C:!a & b << c ^ d | e(a=0, b=2, c=11, d=7, e=5)

!a       = !0 = 1b << c   = 2 << 11 = 40961 & 4096 = 00 ^ 7    = 77 | 5    = 7

结果7

这道题就是第 7 题的"加强版",考的还是同一件事:先算优先级,再按位算

板块 4:位运算应用(第 8、10 题)

第 8 题 → C:异或加解密

for (int i = 0; i < msg.size(); i++) msg[i] = msg[i] ^ key;   // 加密// 再执行一次同样的操作即可还原for (int i = 0; i < msg.size(); i++) msg[i] = msg[i] ^ key;   // 解密

原理:(x ^ k) ^ k = x——异或两次即可还原,这是最经典的对称加密。

第 10 题 → A:位掩码管理权限

const int READ = 1 << 0;     // 0001const int WRITE = 1 << 1;    // 0010const int MODIFY = 1 << 2;   // 0100const int DELETE_ = 1 << 3;  // 1000
目标
写法
记法
置位(加权限)
mask = mask | READ;
或 1 → 变 1
清位(去权限)
mask = mask & (~READ);
与 0 → 变 0

顺序要求:加读 → 加写 →清读→ 加删除,所以选A

干扰项 B、C、D 的错点:把置位写成了 &,或把清位写成了 |,或清位时忘了取反。

板块 5:数组与字符串(第 9、13、14 题)

第 9 题 → A:判断一个"最大字符"能落在哪种进制的合法字符集内

cout << (max <= '1') << ” ” << (max <= '7') << ” ” << (max <= '9') << ” ” << (max <= 'F');
进制
合法字符
判断条件
二进制
0
1
max <= '1'
八进制
0
~`7`
max <= '7'
十进制
0
~`9`
max <= '9'
十六进制
09
AF
max <= 'F'

关键:比较的必须是字符常量(带单引号)。B、C、D 混用了数字字面量(max <= 1)或未带引号的 F(未定义标识符)。

第 13 题 → D(选"错误的")

"s.erase(2) 表示删除字符串中下标为 2 的这一个字符"——

erase(pos) 只有一个参数时,表示从 pos 开始一直删除到字符串末尾;要删单个字符必须写成 erase(pos, 1)。

其余选项均正确:size() 不含 '\0' 且与 length() 等价;empty() 判空;substr(pos) 省略第二参数即截到末尾。

第 14 题 → B:int b['A'];'A' 是字符常量,值为 65,可以作为数组长度 → 合法。

  • A ✘ {1, 2, 3.0} 中 3.0 是 double,收窄转换在 C++11 后属于编译错误(列表初始化禁止收窄);
  • C ✘ int c[]; 没有长度也没有初始化列表;
  • D ✘ float d[10.0] 数组长度必须是整型常量表达式

第 15 题(三进制试毒)→ 关键结论:7 只

每只老鼠有 3 种状态:两轮都不喝 / 第一轮喝 / 第二轮喝(对应"第一轮死""第二轮死""都不死")。

3^k ≥ 1000  →  3^6 = 729 < 10003^7 = 2187 ≥ 1000  →  k = 7

这类题的本质是信息编码:k 只老鼠能区分 3ᵏ 种情况,所以至少要 ⌈log₃1000⌉ = 7 只。三级往年的压轴多是"进制判定程序填空"这类扎实但常规的题,本次换成了信息论味道的建模题,命题思路明显更活了,值得留意。


三、判断题(10 题)

参考答案:× × √ × × × × √ √ ×

关键依据
1
×
补码加法丢弃最高位进位;循环进位是反码的规则
2
×
求补码的扫描法应表述为"第一个 1 及其右边不变左边全部取反",题目把左右说反了
3
8 位原码范围 −127 ~ +127(含 ±0)
4
×
范围 for 的 int num : a 是值的副本num = 0 不影响数组
5
×
(a & 1) == 1
 只能说明 a 是奇数;负数补码最低位也是 1(如 −3 & 1 = 1)
6
×
char str[4] = "GESP"
 需要 5 字节(含 '\0'),数组太小 → 编译报错
7
×
必须比较字符:应写 str[i] == '0' || str[i] == '1'
8
a > 0
 为真触发短路求值b++ 不执行,b 保持 98
9
枚举可借助边界/条件剪枝缩小范围,不必遍历全部
10
×
循环条件 i < n 只累加到 a[n-1]漏掉 a[n],不能算"a[0]~a[n] 全部元素"

三处最典型的坑

  • 第 1、2 题:反码与补码的规则互相"串味"。记住——反码循环进位,补码丢弃进位求补码时,第一个 1 及其右边不动,左边取反
  • 第 6 题char str[4] = "GESP" 是编译错误,不是运行时错误,也不是"自动适配"。
  • 第 5 题& 1 判奇偶对负数同样成立,所以条件成立≠一定是正奇数。

四、编程题

4.1 公共二进制位

题意:给 n 个非负整数,求有多少个二进制位在所有整数中都是 1。某数没有第 i 位时,视为该位为 0。

样例:13、7、15

13 = 1101 7 = 011115 = 1111按位与: 0101  → 第 0 位、第 2 位为 1  →  答案 2

核心思路:全部按位与,再数 1 的个数

int S = a[0];for (int i = 1; i < n; ++i) S &= a[i];   // 统计”所有数都为 1”的位int ans = 0;while (S > 0) { if (S & 1) ans++; S >>= 1; }cout << ans;

三个关键点

  1. "在所有整数中均为 1" ≡ 按位与。这是本题唯一需要想通的地方——& 的语义就是"有 0 则 0"。
  2. 数 1 的个数用 while (S > 0) { ans += S & 1; S >>= 1; } 或 S &= S - 1 均可。
  3. 数据范围:n 可达 2×10⁴,但每个数只需扫一遍,O(n) 足够;用 int 时若数值到 10⁹ 也只占 30 位,安全。

这是三级的一个明显信号:往年位运算主要放在选择题里考,这道题把它升级成了编程题——从"读得懂"变成"写得出"。核心还是那套位运算技巧,只是现在要求你把它完整地实现出来。

4.2 分割字符串

题意:从左到右扫描,第 k 次分割需要连续 k 个空格;每次输出分割后左侧的部分(分隔的空格丢弃),最后输出剩下的右侧部分。无法再分割时结束。

样例 1:sdfd 55 55   99312

轮次
需要的空格
分割结果
输出
第 1 次
1 个
sdfd
 | 55 55   99312
sdfd
第 2 次
2 个
55 55
 |  99312
55 55
第 3 次
3 个
剩下的只有一个空格 → 停止
 99312

注意第 2 次:55 和 55 之间只有 1 个空格不够;55 55 后面有 3 个空格,取走 2 个,所以右侧残留 1 个空格。

样例 2:AbCd   999(3 个空格)

轮次
需要
结果
输出
第 1 次
1 个
AbCd
 |   999
AbCd
第 2 次
2 个
""
 | 999
空行(不能省略!)
第 3 次
3 个
不够 → 停止
999

参考实现

string s = ””;getline(cin, s);                 // 注意:可能含空格,必须用 getlinefor (int k = 1; ; ++k) {    string spaces = string(k, ' ');        // k 个空格    int pos = s.find(spaces);    if (pos == (int)string::nposbreak;    cout << s.substr(0, pos) << endl;      // 输出左侧    s = s.substr(pos + k);                 // 右侧(丢掉 k 个空格)}cout << s << endl;                         // 输出剩余的右侧

四个关键点

  1. 必须用 getline(cin, s)。字符串中含空格,用 cin >> s 只会读到第一个空格为止。
  2. find
     找不到时返回 string::npos,用它判断终止条件。
  3. substr(pos, len)
     / substr(pos) 的用法:substr(0, pos) 是左侧,substr(pos + k) 是右侧。
  4. 空字符串也要输出(样例 2 的空行)。如果写成"左侧非空才输出",第二行就丢了。

本题最大的难点不在算法,而在边界:连续空格的计数、残留空格、空行输出。属于典型的"模拟题,细节定生死"。


五、三级划重点

必须掌握
说明
补码 vs 反码
反码循环进位,补码丢弃进位
补码范围
8 位:−128 ~ 127(原码/反码是 −127 ~ 127)
位运算优先级
~
 > 移位 > & > ^ > |
常用位技巧
x & (x-1)
 消最低位 1;x & (-x) 取最低位 1;x ^ x = 0
置位 / 清位
置位 |,清位 & ~
异或对称性
(x ^ k) ^ k = x
,可用于加密解密
进制与字符集
二/八/十/十六进制各自的合法字符
数组定义语法
长度必须是整型常量;列表初始化禁止收窄转换
string
 成员函数
size / length / empty / substr / find / erase
字符串读入
含空格必须用 getline

抱歉,评论功能暂时关闭!