2026年9月 GESP C++ 三级真题解析:位运算占了半壁江山
三级这次的单选题,有 6 道直接考位运算,加上补码与进制转换,编码类题目接近 10 道。这是三级最近几年最"硬"的一张卷。
一、答案速查
判断题官方未公布答案,以下为按题意推算的参考答案。
二、单选题
板块 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(选"错误的")
5^7^9^7^5 = 9,成对的异或抵消 | |
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; | ||
mask = mask & (~READ); |
顺序要求:加读 → 加写 →清读→ 加删除,所以选A。
干扰项 B、C、D 的错点:把置位写成了
&,或把清位写成了|,或清位时忘了取反。
板块 5:数组与字符串(第 9、13、14 题)
第 9 题 → A:判断一个"最大字符"能落在哪种进制的合法字符集内
cout << (max <= '1') << ” ” << (max <= '7') << ” ” << (max <= '9') << ” ” << (max <= 'F');01 | max <= '1' | |
0 | max <= '7' | |
0 | max <= '9' | |
09AF | 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 < 1000,3^7 = 2187 ≥ 1000 → k = 7这类题的本质是信息编码:k 只老鼠能区分 3ᵏ 种情况,所以至少要
⌈log₃1000⌉ = 7只。三级往年的压轴多是"进制判定程序填空"这类扎实但常规的题,本次换成了信息论味道的建模题,命题思路明显更活了,值得留意。
三、判断题(10 题)
参考答案:× × √ × × × × √ √ ×
for 的 int num : a 是值的副本,num = 0 不影响数组 | ||
(a & 1) == 1 | ||
char str[4] = "GESP"'\0'),数组太小 → 编译报错 | ||
str[i] == '0' || str[i] == '1' | ||
a > 0b++ 不执行,b 保持 98 | ||
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 = 11017 = 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" ≡ 按位与。这是本题唯一需要想通的地方—— &的语义就是"有 0 则 0"。数 1 的个数用 while (S > 0) { ans += S & 1; S >>= 1; }或S &= S - 1均可。数据范围:n 可达 2×10⁴,但每个数只需扫一遍,O(n) 足够;用 int时若数值到 10⁹ 也只占 30 位,安全。
这是三级的一个明显信号:往年位运算主要放在选择题里考,这道题把它升级成了编程题——从"读得懂"变成"写得出"。核心还是那套位运算技巧,只是现在要求你把它完整地实现出来。
4.2 分割字符串
题意:从左到右扫描,第 k 次分割需要连续 k 个空格;每次输出分割后左侧的部分(分隔的空格丢弃),最后输出剩下的右侧部分。无法再分割时结束。
样例 1:sdfd 55 55 99312
sdfd55 55 99312 | sdfd | ||
55 55 99312 | 55 55 | ||
99312 |
注意第 2 次:
55和55之间只有 1 个空格不够;55 55后面有 3 个空格,取走 2 个,所以右侧残留 1 个空格。
样例 2:AbCd 999(3 个空格)
AbCd 999 | AbCd | ||
""999 | |||
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::npos) break;cout << s.substr(0, pos) << endl; // 输出左侧s = s.substr(pos + k); // 右侧(丢掉 k 个空格)}cout << s << endl; // 输出剩余的右侧
四个关键点
必须用 getline(cin, s)。字符串中含空格,用cin >> s只会读到第一个空格为止。find找不到时返回 string::npos,用它判断终止条件。substr(pos, len)/ substr(pos)的用法:substr(0, pos)是左侧,substr(pos + k)是右侧。空字符串也要输出(样例 2 的空行)。如果写成"左侧非空才输出",第二行就丢了。
本题最大的难点不在算法,而在边界:连续空格的计数、残留空格、空行输出。属于典型的"模拟题,细节定生死"。
五、三级划重点
~& > ^ > | | |
x & (x-1)x & (-x) 取最低位 1;x ^ x = 0 | |
|,清位 & ~ | |
(x ^ k) ^ k = x | |
string | size / length / empty / substr / find / erase |
getline |