ARTICLE · 1085885
GESP 2026年9月 C++ 三级真题,答案与知识点解析
一、单选题(每题 2 分,共 30 分)
第 1 题
关于计算机的数据编码,下面说法不正确的是( )。
A. 数字 5,不管采用原码、反码、补码,它的 8 位二进制形式都是 (0000 0101)₂。
B. 计算机科学中,单位换算默认 1KB = 1024B,而非 1000B。
C. 原码是最直观的一种有符号数表示方法。
D. 反码运算中,最高位进位要抛掉。
答案:D
知识点解析
本题考查原码、反码、补码的基本概念与运算规则。
- A 正确:正数的原码、反码、补码完全相同,5 的 8 位二进制形式都是 (0000 0101)₂。
- B 正确:计算机存储单位按二进制换算,1KB = 1024B。
- C 正确:原码用最高位表示符号、其余位表示绝对值,是最直观的有符号数表示方法。
- D 错误:反码加法运算中最高位产生的进位不能抛掉,而要按“循环进位”规则加回最低位;补码运算才是直接舍弃最高位进位。故选 D。
第 2 题
关于进制转换,下面说法不正确的是( )
A. 数字 1101B 可以分解为:1 × 2³ + 1 × 2² + 0 × 2¹ + 1 × 2⁰ = 8 + 4 + 0 + 1 = 13(十进制)
B. 数字 42Q 可以分解为:4 × 8¹ + 2 × 8⁰ = 32 + 2 = 34(十进制)
C. 数字 2BEH 可以分解为:2 × 16² + 11(B) × 16¹ + 14(E) × 16⁰ = 512 + 176 + 14 = 702(十进制)
D. 345Q = 0E3H(通常写作 E3H)
答案:D
知识点解析
本题考查二、八、十六进制与十进制的互转。
- A 正确:1101B 按权展开 1×2³+1×2²+0×2¹+1×2⁰ = 13。
- B 正确:42Q = 4×8¹ + 2×8⁰ = 34。
- C 正确:2BEH = 2×16² + 11×16¹ + 14×16⁰ = 512 + 176 + 14 = 702。
- D 错误:345Q = 3×8² + 4×8¹ + 5 = 192 + 32 + 5 = 229,而 229 = 14×16 + 5,对应十六进制应为 E5H,不是 E3H。故选 D。
第 3 题
下面程序的运行结果是( )
#include <iostream>
using namespace std;
int main() {
int x = 1;
for (int i = 0; i = 0; i <= 5) {
x++;
}
cout << x << endl;
return 0;
}
A. 1
B. 10
C. 15
D. 程序编译错误
答案:A
知识点解析
本题考查 for 循环中条件表达式的求值。
for 圆括号中的第二个表达式是循环条件,这里写的 i = 0 是赋值表达式,其表达式的值为 0,表示逻辑“假”,因此循环体一次都不会执行,x 保持初值 1。这种写法在 C++ 中合法(容易与 i == 0 混淆,编译器可能给警告,但不会编译错误)。注意区分 i = 0(赋值,值为 0)与 i == 0(比较判断)。故输出 1,选 A。
第 4 题
关于计算机编码中补码,下面说法错误的是( )。
A. 正数的补码:与其原码、反码相同。
B. 负数的补码:将其对应的正数的原码按位取反(得到反码),然后加 1。
C. 8 位的补码,最大表示数据是 128,最小表示数据是 −128。
D. 负数的补码,从右往左扫描正数的二进制形式,遇到第一个 1 之后,左边的所有位都取反。
答案:C
知识点解析
本题考查 8 位补码的表示范围。
8 位补码的表示范围是 −128 ~ +127:最高位是符号位,其余 7 位表示数值,最大值为 0111 1111 即 +127,最小值为 1000 0000 即 −128。C 选项说最大表示 128,错误——128 超出了 8 位补码的表示能力(+128 需要 9 位才能表示)。A、B 是补码的基本求法,均正确;D 是求负数补码的快捷扫描方法,也正确。故选 C。
第 5 题
下面程序执行的结果是( )。
#include <iostream>
using namespace std;
int main() {
int x = 2026;
int count = 0;
while (x > 0) {
count++;
x = x & (x - 1);
}
cout << count << endl;
}
A. 2026
B. 8
C. 16
D. 32
答案:B
知识点解析
本题考查位运算 x & (x−1) 的经典应用。
x & (x - 1) 每执行一次,会把 x 二进制中最低位的 1 变为 0(例如 12(1100) & 11(1011) = 8(1000)),因此 while 循环的执行次数恰好等于 x 二进制表示中 1 的个数。2026 = (11111101010)₂,逐位数出共有 8 个 1,故输出 8,选 B。
第 6 题
关于位运算,下列说法错误的是( )。
A. 找唯一数:数组中唯一出现一次的数,其余出现两次,全部异或结果即为该数。
例子:数组 [5, 7, 9, 7, 5](唯一数是 9)
B. 若左移导致高位溢出(如超过整型范围),结果不再符合乘法规律。
C. 有符号整数左移溢出时结果确定。
D. n + 2 位二进制数 A₁A₂⋯Aₙ00,一定是 4 的倍数(最后两位 00 是二进制的 00)。
答案:C
知识点解析
本题考查左移运算的溢出特性与未定义行为。
C++ 中有符号整数左移导致溢出(如符号位改变、超出表示范围)属于未定义行为,不同编译器、不同平台得到的结果可能不同,因此“结果确定”的说法错误。A 正确:异或运算中相同的数两两抵消(a ^ a = 0),全部异或后剩下的就是只出现一次的数;B 正确:高位溢出后数值被截断,不再满足“左移一位相当于乘 2”的规律;D 正确:二进制末两位是 00 说明该数等于某个数左移两位,一定是 4 的倍数。故选 C。
第 7 题
计算 3 & 7 | 15 ^ ~11(a=3, b=7, c=15, d=11):( )。
A. -9
B. -1
C. -7
D. -5
答案:D
知识点解析
本题考查位运算符的优先级:~ 高于 &,& 高于 ^,^ 高于 |。
代入 a=3, b=7, c=15, d=11 逐步计算:① 3 & 7 = (0011)₂ & (0111)₂ = 3;② ~11 = −12(按位取反等价于 −(11+1));③ 15 ^ (−12):15 为 0000 1111,−12 的 8 位补码为 1111 0100,异或得 1111 1011,即 −5;④ 3 | (−5) = 0011 | 1111 1011 = 1111 1011,仍为 −5。故结果为 −5,选 D。
第 8 题
下面函数是一个加密解密的函数,横线处应该填入的程序段是( )。
#include <iostream>
#include <string>
using namespace std;
int main() {
int key;
cin >> key;
string msg = "Hello GESP";
cout << msg << endl;
for (int i = 0; i < msg.size(); i++) {
msg[i] = msg[i] ^ key;
}
cout << msg << endl;
_________________________________
cout << msg << endl;
return 0;
}
A.
for (int i = 0; i < msg.size(); i++) {
msg[i] = msg[i] | key;
}
B.
for (int i = 0; i < msg.size(); i++) {
msg[i] = msg[i] >> key;
}
C.
for (int i = 0; i < msg.size(); i++) {
msg[i] = msg[i] ^ key;
}
D.
for (int i = 0; i < msg.size(); i++) {
msg[i] = msg[i] & key;
}
答案:C
知识点解析
本题考查异或运算的自反性:a ^ k ^ k = a。
加密时每个字符与 key 异或得到密文;解密时对密文再异或同一个 key 即可还原明文,因此横线处应重复“与 key 异或”的操作,选 C。而或运算、与运算都会不可逆地丢失二进制位信息(如某位与 0 相与后置 0,无法恢复),移位运算也会丢失移出的位,均无法还原原文。
第 9 题
下列程序是分别判断输入的 N 个数是否可能是二进制、八进制、十进制、十六进制,横线处应该填入的是( )
#include <iostream>
using namespace std;
int main() {
int n = 0;
cin >> n;
for (int i = 0; i < n; i++) {
char str[11];
cin >> str;
char max = '0';
for (int i = 0; str[i] != '\0'; i++)
if (str[i] > max)
max = str[i];
_____________________________
}
return 0;
}
A. cout << (max <= '1') << " " << (max <= '7') << " " << (max <= '9') << " " << (max <= 'F') << endl;
B. cout << (max <= 1) << " " << (max <= 7) << " " << (max <= 9) << " " << (max <= 'F') << endl;
C. cout << (max <= 1) << " " << (max <= 7) << " " << (max <= 9) << " " << (max <= F) << endl;
D. cout << (max >= 1) << " " << (max >= 7) << " " << (max >= 9) << " " << (max >= F) << endl;
答案:A
知识点解析
本题考查字符比较与进制数字范围的判断。
判断一个数字串能否是某种进制的数,只需检查每一位字符是否都不超过该进制允许的最大数字字符:二进制每位只能是 ‘0’/‘1’,八进制不超过 ‘7’,十进制不超过 ‘9’,十六进制还需允许 ‘A’~‘F’。max 是字符串中最大的字符,故用 max 与字符常量 ‘1’、‘7’、‘9’、‘F’ 依次比较即可,选 A。B、C 用整数 1、7、9 与字符比较,语义错误:字符 ‘0’ 的 ASCII 码是 48,恒大于 1、7、9,前三个比较永远为假,即使输入合法也会被误判;C 中 F 未加引号会被当作未声明的标识符,编译报错;D 的比较方向完全反了。
第 10 题
下面的程序是位掩码技术实现权限的组合管理。首先定义四个权限常量,分别对应读、写、修改、删除,每个权限占用整数的一个独立二进制位。初始化权限掩码 mask 为 0,代表没有任何权限。如果想实现如下的操作:添加读权限;添加写权限;清除读权限,其他位不变;添加删除权限。则横线处应该填入的是( )。
#include <iostream>
using namespace std;
int main() {
const int READ = 1 << 0;
const int WRITE = 1 << 1;
const int MODIFY = 1 << 2;
const int DELETE_ = 1 << 3;
int mask = 0;
__________________________
return 0;
}
A.
mask = mask | READ;
mask = mask | WRITE;
mask = mask & (~READ);
mask = mask | DELETE_;
B.
mask = mask & READ;
mask = mask & WRITE;
mask = mask | (~READ);
mask = mask & DELETE_;
C.
mask = mask | READ;
mask = mask & WRITE;
mask = mask & (~READ);
mask = mask | DELETE_;
D.
mask = mask | READ;
mask = mask | WRITE;
mask = mask | (~READ);
mask = mask | DELETE_;
答案:A
知识点解析
本题考查位掩码技术:置 1 用“或”,清 0 用“与 ~”。
添加权限用 mask | 权限位(把对应位或成 1,其余位不变);清除读权限且不影响其他位必须用 mask & (~READ),即把 READ 对应位清 0、其余位保持。A 依次完成加读、加写、清读、加删,符合要求。B 用 & READ、& WRITE 会把其他权限位全部清掉;C 第二步 mask & WRITE 把刚加上的读权限清掉了;D 中 mask | (~READ) 会把除读以外所有位都置 1,权限全乱。故选 A。
第 11 题
下面选项中提到的变量都是正整数,关于位运算,下面说法错误的是( )。
A. num >> 1,对于正整数,等价于对 num 做整数除以 2,向下取整
B. num | 0x0F,可以把 num 的低 4 位全部置为 1,高位保持不变
C. num ^ num 的结果一定等于 0
D. 若 num 右移时低位被移出丢弃,结果一定符合除法数学规律
答案:D
知识点解析
本题考查右移运算与除法的关系及各位运算的性质。
A 正确:对正整数,右移一位等价于除以 2 并向下取整;B 正确:0x0F 低 4 位全为 1,按位或后低 4 位全被置 1,高位不受影响;C 正确:任何数与自身按位异或,每一位都抵消,结果一定为 0;D 错误:右移丢弃低位会损失精度,例如 num = 5 时 5 >> 1 = 2,而 5 ÷ 2 = 2.5,结果并不符合精确的数学除法规律。故选 D。
第 12 题
已知:a=0, b=2, c=11, d=7, e=5,表达式 !a & b << c ^ d | e 的结果是( )
A. 5
B. 13
C. 7
D. 28
答案:C
知识点解析
本题考查运算符优先级:! 高于移位 <<,<< 高于 &,& 高于 ^,^ 高于 |。
代入 a=0, b=2, c=11, d=7, e=5 逐步计算:① !a = !0 = 1;② b << c = 2 << 11 = 4096;③ 1 & 4096 = 0(4096 = (1 0000 0000 0000)₂,第 0 位是 0);④ 0 ^ 7 = 7;⑤ 7 | 5 = (111)₂ | (101)₂ = (111)₂ = 7。故结果为 7,选 C。
第 13 题
关于 string 的成员函数,下面说法错误的是( )
A. size() 返回字符串的字符个数,不包含末尾的 '\0';length() 和 size() 功能完全等价
B. s.empty():字符串为空时返回 true,字符串不为空返回 false
C. s.substr(pos, len),当省略第二个参数 len 时,代表从 pos 位置一直截取到字符串末尾
D. s.erase(2) 表示删除字符串中下标为 2 的这一个字符
答案:D
知识点解析
本题考查 string 常用成员函数的语义。
A、B、C 均正确:size() 返回字符个数且不含末尾 ‘\0’,与 length() 完全等价;empty() 判空返回 bool;substr(pos) 省略第二个参数时从 pos 一直截取到末尾。D 错误:单参数的 s.erase(2) 表示删除从下标 2 开始直到字符串末尾的所有字符,若只想删除下标为 2 的一个字符,应写成 s.erase(2, 1)。故选 D。
第 14 题
以下数组定义,符合 C++ 语法的是( )
A. int a[] = {1, 2, 3.0};
B. int b['A'];
C. int c[ ];
D. float d[10.0];
答案:B
知识点解析
本题考查数组定义的语法规则。
B 正确:‘A’ 是字符常量,参与运算时取其 ASCII 码 65,int b[‘A’] 等价于 int b[65],合法。A 错误:用列表初始化时 3.0 到 int 属于窄化转换,C++ 编译报错;C 错误:既未指定大小也没有初始化列表,编译器无法确定数组长度,不合法;D 错误:数组长度必须是整型常量表达式,10.0 是浮点数,不合法。故选 B。
第 15 题
三进制有个奇妙的应用,有 1000 个酒缸,恰好 1 缸有毒。毒酒喝下后 24 小时毒发死亡;无毒酒无影响。总共有 48 小时,可以做两轮实验,每轮可以重新安排老鼠喝酒。有三种情况,这只老鼠两轮都不喝这缸酒;这只老鼠第一轮喝这缸酒;这只老鼠第二轮喝这缸酒,要保证找出毒酒,最少需要多少只老鼠?( )
A. 5
B. 7
C. 10
D. 9
答案:B
知识点解析
本题考查三进制思想在信息编码类推理中的应用。
每只老鼠对每一缸酒有 3 种状态:两轮都不喝、第一轮喝、第二轮喝,相当于三进制的一个数位,因此 k 只老鼠一共能区分 3^k 缸酒。要求 3^k ≥ 1000,而 3^6 = 729 < 1000,3^7 = 2187 ≥ 1000,故最少需要 7 只老鼠,选 B。本质是:k 位三进制数可以表示 3^k 个不同的状态组合。
二、判断题(每题 2 分,共 20 分)
第 1 题
对于计算机编码运算,补码做加法运算时,如果最高位产生进位,需要把该进位循环加到结果的最低位。
答案:×
知识点解析
本题考查补码与反码加法运算规则的区别。
“最高位进位循环加到最低位”是反码加法运算的规则;补码加法运算中最高位产生的进位直接丢弃即可,结果仍然正确。题目把反码的规则说成了补码,故说法错误。
第 2 题
求 −13 的补码,可以对正数 13 的二进制,从右向左扫描,遇到第一个 1 之后,右边所有位全部取反,左边保持不变。
答案:×
知识点解析
本题考查负数补码的快速求法,题目把方向说反了。
正确方法:从右往左扫描正数的二进制形式,遇到第一个 1 时,该 1 及其右边的位保持不变,左边的所有位取反。例如 13 为 0000 1101,最低位的 1 及其右边保持不变,左边取反得 1111 0011,即 −13 的补码。题目说“右边取反、左边不变”,与正确规则相反,故说法错误。
第 3 题
一个 8 位二进制原码表示的有符号数,可表示的数值范围是 −128 到 +127。
答案:×
知识点解析
本题考查 8 位原码的表示范围。
8 位原码中最高位是符号位,其余 7 位表示绝对值,可表示的范围是 −127 ~ +127,且 0 有 +0 和 −0 两种表示,范围不对称;而 −128 ~ +127 是 8 位补码的表示范围。题目把补码的范围套在了原码上,故说法错误。
第 4 题
这段代码执行后,能够将数组中所有大于 0 的元素赋值为 0。
int a[] = {2, -5, 7, 0, -1};
int n = sizeof(a) / sizeof(a[0]);
for (int num : a) {
if (num > 0) {
num = 0;
}
}
答案:×
知识点解析
本题考查范围 for 循环中循环变量的拷贝语义。
for (int num : a) 中的 num 是数组元素的副本,修改 num 只是修改这个副本,数组元素本身不会被改变,循环结束后数组仍是 {2, -5, 7, 0, -1}。若要真正修改元素,应使用引用:for (int &num : a)。故说法错误。
第 5 题
a 是 int 有符号整型,若 (a & 1) == 1 结果为 true,则 a 一定是正奇数。
答案:×
知识点解析
本题考查按位与判断奇偶的方法及补码下的符号问题。
(a & 1) == 1 只能说明 a 的二进制最低位是 1,即 a 是奇数;在补码表示下,负奇数(如 −3 的补码 …11111101)的最低位同样是 1,同样满足该条件。因此 a 也可能是负奇数,不一定是正奇数,故说法错误。
第 6 题
char str[4] = "GESP"; 将在内存空间中给 str[4] 分配 5 个字节。
答案:×
知识点解析
本题考查字符数组大小与字符串结束符 ‘\0’。
字符串 “GESP” 占 4 个字符,还必须额外存放结束符 ‘\0’,共需 5 个字节;而 char str[4] 只分配 4 个字节的空间,根本装不下 “GESP”,这条初始化语句本身就不合法(编译报错),更谈不上分配 5 个字节。故说法错误。
第 7 题
判断字符串中字符是否是 '0' 或者 '1',可以写条件:if(str[i]==0 || str[i]==1) 。
答案:×
知识点解析
本题考查字符常量与整数的关系。
字符 ‘0’ 的 ASCII 码是 48,‘1’ 的 ASCII 码是 49,写成 str[i]==0 || str[i]==1 是在与整数 0、1 比较,对字符 ‘0’、‘1’ 来说永远不成立。正确写法是 if (str[i]=='0' || str[i]=='1'),字符常量必须加单引号。故说法错误。
第 8 题
下面程序执行后,将输出 98 。
#include <iostream>
using namespace std;
int main() {
int a = 'a', b = 'b';
if (a > 0 || b++ > 0) {
}
cout << b;
}
答案:√
知识点解析
本题考查逻辑或 || 的短路求值特性。
a = ‘a’,其 ASCII 码为 97,条件 a > 0 为真;根据 || 的短路规则,只要左操作数为真就不再计算右操作数,因此 b++ > 0 不会执行,b 保持初值 98(‘b’ 的 ASCII 码),程序输出 98。故说法正确。
第 9 题
枚举可以在保证不漏解的前提下缩小枚举范围,不是必须遍历全部。
答案:√
知识点解析
本题考查枚举算法的范围优化。
枚举并不等于盲目遍历所有可能,在能够证明不漏解的前提下,可以缩小枚举范围或跳过明显不可能的候选,例如判断 n 是否为素数只需枚举 2 ~ √n 的因子。这样既保证正确性又显著提高效率。故说法正确。
第 10 题
下面流程图逻辑:初始化 sum ← 0,i ← 0;当 i < n 成立时,执行 sum ← sum + a[i],再执行 i ← i + 1;循环结束输出 sum。该流程图可以正确计算一维数组 a[0] ~ a[n] 全部元素的总和。
答案:×
知识点解析
本题考查循环边界条件与数组下标的对应关系。
流程图中循环条件是 i < n,循环体只把 a[0] 到 a[n−1] 累加进 sum;当 i 增到 n 时条件不成立退出,a[n] 从未被累加。要正确计算 a[0] ~ a[n] 全部元素的总和,循环条件应为 i <= n。故说法错误。
三、编程题(每题 25 分,共 50 分)
公共二进制位
时间限制 1.0 s 内存限制 512.0 MB
题目描述
小红有 n 个非负整数 a₁, a₂, ⋯, aₙ。她将每个整数转换为二进制后,想知道有多少个二进制位在所有整数中均为 1。
二进制位从右向左编号为 0, 1, 2, ⋯。若某个整数的二进制表示中没有第 k 位,则认为它的第 k 位为 0。
请你求出满足条件的二进制位数量。
输入格式
- 第一行一个整数 n,表示整数的个数。
- 第二行 n 个非负整数 a₁, a₂, ⋯, aₙ。
输出格式
输出一个整数,表示所有整数的二进制表示中均为 1 的二进制位数量。
样例
3
13 7 15
2
样例解释 1
三个整数的二进制表示分别为 (1101)₂、(0111)₂ 和 (1111)₂。
其中第 0 位和第 2 位均为 1,因此答案为 2。
数据范围
2 ≤ n ≤ 20000,0 ≤ aᵢ ≤ 10⁹。
参考程序(答案)
#include <iostream>
using namespace std;
int a[20010];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; ++i)
cin >> a[i];
int S = a[0];
for (int i = 1; i < n; ++i)
S &= a[i];
int ans = 0;
while (S > 0) {
if (S & 1) ans++;
S >>= 1;
}
cout << ans;
return 0;
}
分割字符串
时间限制 1.0 s 内存限制 512.0 MB
题目描述
小明拿到了一段字符串文本,小明希望用下面的规则分割这个字符串:
1. 小明从左到右扫描字符串,遇到第一个空格后停止,并将字符串分割为左右两个部分(舍弃用于分割的这个空格);
2. 对于上次分割后右侧的字符串,小明继续扫描,第一次遇到连续两个空格后停止,并将字符串分割为左右两个部分(舍弃用于分割的这两个空格);
3. 对于上次分割后右侧的字符串,小明继续扫描,第一次遇到连续三个空格后停止,并将字符串分割为左右两个部分(舍弃用于分割的这三个空格);
4. 以此类推,小明每分割一次,下一次分割就需要更长的连续空格;直到字符串无法再次分割。
最后输出每次分割的左侧的字符串,以及最后一次分割的右侧字符串。
如果字符串完全无法进行任何一次分割,直接输出其本身。
输入格式
输入一行,一个字符串。
输出格式
输出若干行。每行为分割后的一个字符串,注意依次输出:
第一次分割左侧的字符串,第二次分割左侧的字符串,...,最后一次分割左侧的字符串,最后一次分割右侧的字符串。
样例
sdfd 55 55 99312
sdfd
55 55
99312
样例解释 1
1. 第一次分割将字符串分割为 sdfd 和 55 55 99312;
2. 第二次分割将字符串分割为 55 55 和 99312。
对于第二次分割,注意 55 和 55 之间虽然有空格,但是第二次分割需要连续两个空格才行;
另外,55 55 和 99312 之间有三个空格,第二次分割只需要两个,所以会分割为 55 55 和 99312,也就是右侧会留下一个空格;因为只留下了一个空格,所以不再满足第三次分割要求。
AbCd 999
AbCd
999
样例解释 2
1. 第一次分割将字符串分割为 AbCd 和 999;
2. 第二次分割将字符串分割为 空字符串 和 999。
由于 AbCd 和 999 之间有三个空格,第一次分割消耗掉一个,因此还能进行第二次分割,这个时候会分割出空字符串,因此输出中会输出一个空行(不能省略)。
数据范围
字符串长度不超过 1000,字符串中只包含大写字母、小写字母,数字,或空格,且输入字符串不会以空格结尾。
特别强调:输入仅一行,但可能包含若干空格
参考程序(答案)
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "";
getline(cin, s);
for (int k = 1;; ++k) {
string spaces = "";
for (int i = 0; i < k; ++i) {
spaces += " ";
}
int pos = s.find(spaces);
if (pos < 0 || pos >= s.size()) break;
for (int i = 0; i < pos; ++i) {
cout << s[i];
}
cout << endl;
string rest = "";
for (int i = pos + k; i < s.length(); ++i) {
rest += s[i];
}
s = rest;
}
cout << s << endl;
return 0;
}
由于工作量较大,若存在错漏欢迎大家评论区指正。祝各位考生顺利通过!觉得有用,欢迎点赞、在看、转发三连。