GESP C++一级易错点:运算符优先级、输入输出与循环边界
简介:这是一份面向GESP C++一级考生及少儿编程教师的2024年3月真题文档,适合考前自测、课堂讲评与基础语法查漏补缺,对初学者巩固语法、教师组织模拟测评也较实用。压缩包共1个docx文件,约133KB,采用可编辑试卷排版,便于打印、批注和二次整理。内容覆盖表达式运算、输入输出、条件分支、循环结构、数组累加、类型转换、编译流程、操作系统与计算机常识等一级考点,包含15道单选题、10道判断题,以及“小杨买书”“找因数”两道编程题,能帮助读者熟悉题型分布、分值结构与命题难度。目前已有1538人学习下载,具备一定参考热度。借助完整题干与答案页,读者可按选择、判断、编程三个模块分块练习,对照错题定位薄弱知识点,并围绕取模、循环累加、因数判断等常见写法进行针对性复习,形成较系统的备考路径。
1. 知识地图:GESP C++ 一级真正考的是细节精度
GESP C++ 一级的卷面结构相当固定:15 道单选、10 道判断、2 道编程题,两道编程题各 25 分、合计 50 分,客观题与上机题基本对半分。很多人以为一级就是背语法,真正拉开差距的是细节精度——一个空格、一次边界、一种类型转换。信奥赛这条路上,一级是入门关卡,它不考算法,考的是你对 cin/cout、运算符优先级、for 与 while 的掌控是否真到位。这份笔记按「表达式 → 输入输出 → 分支循环 → 易错点 → 编程题」把整张卷子重排了一遍,适合刚学完顺序、分支、循环三件套、准备第一次上考场的人。
2. 表达式与运算符:优先级、取模、类型转换三件套
整张卷子里,跟表达式相关的题目能占到客观题的三分之一。它们看起来最没技术含量,实际上是失分重灾区,因为选项往往只差一个字符或者一次隐式转换。把这一块拆成四件事:谁先算、取模怎么算、类型怎么变、真假怎么判。
2.1 运算符优先级:括号是唯一不用背的东西
卷面第一题就是(3 - 2) * 3 + 5。括号先算得 1,乘 3 得 3,加 5 得 8。这题本身不难,难的是后面那些没有括号、全靠优先级和结合性说话的表达式。一级需要掌握的层级其实不多,按从高到低排下来是:括号、单目运算符(!、-、++、--)、* / %、+ -、关系运算(< > <= >=)、相等判断(== !=)、逻辑与&&、逻辑或||、赋值=。
| 优先级 | 运算符 | 结合方向 | 一级常见考法 |
|---|---|---|---|
| 1 | () | 从左到右 | 强制改变计算顺序 |
| 2 | !-(负号)++-- | 从右到左 | -a + b先取负 |
| 3 | */% | 从左到右 | 10 / 4 * 2得 4 不是 1.25 |
| 4 | +- | 从左到右 | 10 - 4 - 3得 3 |
| 5 | <><=>= | 从左到右 | 与算术混用时的顺序 |
| 6 | ==!= | 从左到右 | 判断结果只有 0 和 1 |
| 7 | && | 从左到右 | 短路求值 |
| 8 | ` | ` | |
| 9 | = | 从右到左 | a = b = 3 |
真正需要背的只有一条:同级运算符从左往右算。减法、除法、取模这三兄弟最容易被当成「可以随便换顺序」,10 - 4 - 3从左算是 3,先算右边就变成 9,这种错误在笔试里几乎每年都有变体。
#include <iostream> using namespace std; int main() { cout << (3 - 2) * 3 + 5 << endl; // 8:括号 → 乘法 → 加法 cout << 10 - 4 - 3 << endl; // 3:同级左结合,先算 10-4 cout << 10 / 4 * 2 << endl; // 4:先整除得 2,再乘 2 cout << 2 + 3 * 4 % 5 << endl; // 4:先 3*4=12,12%5=2,再加 2 return 0; }这几行输出的差异全部来自「先算谁」。第三行如果换成10 / (4 * 2)结果就是 1,第四行如果把%换成-结果立刻变。考场上判断这类题,稳妥做法是拿笔在表达式下面画分层线,一层一层往下剥,不要心算。
2.2 取模运算:%只认整数,符号跟着被除数走
第二题cout << "5%2=" << 5 % 2,取模得 1,前面那串字符串原样输出,最终是5%2=1。取模的本质是求余数,27 % 10得 7 这也是判断题第一题的原型。一级关于%的考点集中在三处:只能用于整数、结果范围、负数怎么办。
a % b的结果一定落在0到b-1之间(a、b都为正时)。这个性质是做数位分离题的基础,比如n % 10拿个位、n / 10 % 10拿十位。负数取模在 C++ 里的规则是「向零取整,余数符号与被除数一致」,-7 % 3得-1,7 % -3得1。一级一般不深挖负数,但判断题里会拿「取模结果一定小于除数」这种表述做文章。
#include <iostream> using namespace std; int main() { int n = 275; cout << n % 10 << endl; // 5:个位 cout << n / 10 % 10 << endl; // 7:十位,先整除去掉个位 cout << n % 100 << endl; // 75:末两位 cout << -7 % 3 << endl; // -1:余数符号跟被除数 return 0; }注意n / 10 % 10的执行顺序:/和%同级且左结合,所以先整除再取模,拿到的是十位而不是别的位。这个写法在真题里反复出现,写反一次就会拿到个位,而编译器一声不吭。
2.3 类型与赋值:3.0 和 3 值相等,但不是一回事
判断题第七题的原文是「3.0 和 3 的值相等,所以它们占用的存储空间也相同」,答案是错。3.0是double,一般占 8 字节;3是int,一般占 4 字节。值相等和类型相同是两码事,这是整张卷子里最容易被「常识」带偏的一题。
选择题第十一题问哪个赋值语句不正确,四个选项里x=(3.16)、x=3.16、x=int(3.16)都能编过,只有x=3.16 int这种写法是语法错误——类型名不能跟在数值后面。这题真正想让你分清的是两件事:int变量接收浮点数时会截断(不是四舍五入),以及强制类型转换的两种写法(int)3.16和int(3.16)都合法。
#include <iostream> using namespace std; int main() { int x = 3.99; // 截断为 3,不是 4 double y = 3; // 升级为 3.0,占 8 字节 int z = (int)3.99; // C 风格强转,结果同样是 3 cout << x << " " << y << " " << z << endl; // 3 3 3 cout << sizeof(x) << " " << sizeof(y) << endl; // 4 8 return 0; }sizeof的具体字节数在不同平台上可能不同,考试不会让你背死数字,但「整型比浮点型占得少」这个方向必须清楚。另一个高频坑是int除法:7 / 2得 3 而不是 3.5,想要小数必须有一边是浮点型,比如7 / 2.0。
2.4 逻辑真假:取模结果可以直接当条件用
第八题里出现了if(i%3 && i%7)这种写法,没有写!= 0。C++ 里非零即真,i%3的结果是 0 就为假,是 1 或 2 就为真。所以这个条件的含义是「i 既不能被 3 整除,也不能被 7 整除」,两个条件同时成立才累加。判断题第四题说"10"*2会报错,理由是字符串和整数不能一起运算——这题对,字符串字面量是const char[],跟整数相乘没有定义。
&&和||还有短路特性:A && B里 A 为假就不再算 B,A || B里 A 为真也不再算 B。一级不考带副作用的短路题,但会让你判断「a > 0 && b / a > 1这种写法安不安全」,答案是因为短路,a为 0 时右边根本不会执行。
3. 输入输出:cout、printf、cin、scanf 的四个坑
第四题和第五题是同一段逻辑的两种写法,输出却不一样,这种题放在一起就是为了让你看清「看起来一样」和「真的一样」之间的距离。输入输出这一块,一级的要求不高但极细:拼接顺序、字符串里的空格、格式符、输入函数的职责边界,每一项都能单独出一道题。
3.1 cout 的拼接:空格藏在字符串里,不在运算符里
第四题的代码是cout << "a+1= " << a+1 << endl;,注意等号后面那个空格,它属于字符串的一部分。所以输出是a+1= 2,等号和 2 之间有一个空格。第五题换成printf("a+1=%d\n", a+1);,格式串里等号后面直接跟%d,输出就是a+1=2,没有空格。同一段计算逻辑,输出差一个字符,选项里四个答案就围着这个空格设。
#include <iostream> #include <cstdio> using namespace std; int main() { int a = 1; cout << "a+1= " << a + 1 << endl; // a+1= 2,等号后有一个空格 printf("a+1=%d\n", a + 1); // a+1=2,等号后无空格 cout << "a+1=" << " " << a + 1 << endl; // a+1= 2,效果同第一行 return 0; }三条语句的差别全在字符串常量里。判断这种题的方法很笨但有效:把输出内容在草稿纸上逐字符抄一遍,包括空格,然后跟选项比对。抄的时候用竖线标出每个字符的位置,比盯着看靠谱得多。
3.2 printf 的格式串:%d是占位符,\n是换行
判断题第二题printf("%d#%d&",2,3)输出2#3&,两个%d依次被 2 和 3 替换,#和&是普通字符原样输出,末尾没有\n所以不换行。printf的格式串里,%d对应整型、%c对应字符、%f对应浮点、%%输出一个百分号本身,\n是换行符而不是两个字符。
一级常考的对比是「cout需不需要关心类型」:cout会自动识别类型,printf必须自己写对格式符,写错了不会报错但会输出乱值。这也是为什么初学阶段更推荐cout,但考试里两种都会出现,必须都能读。
3.3 cin 读入5+2:读到第一个不合法字符就停
第三题是这张卷子里设计得最巧的一道:执行cin >> a时输入5+2,会发生什么。四个选项里没有「a 等于 5」,最终答案是「依赖于变量 a 的类型」,因为如果a是int,cin读到+就停止,a被赋值为 5,+2留在缓冲区;如果a是string,整个5+2会被当成字符串读进去;如果a压根没定义,编译期就过不去。
#include <iostream> using namespace std; int main() { int a = 0; cin >> a; // 输入:5+2 cout << a << endl; // 5,遇到 '+' 停止读取 char op; cin >> op; // 接着从缓冲区读到 '+' cout << op << endl; // + return 0; }cin >>的规则可以概括成:跳过前导空白,按目标类型读尽可能多的合法字符,遇到不合法字符立刻停手并把该字符留在缓冲区。所以5+2不会报错,也不会算出 7,只会安静地给你一个 5。理解了这一点,后面学字符串读入时那些「多读了一个换行」的玄学问题就都有了解释。
3.4 编程题的输入输出规范:多一个字就是零分
两道编程题的题干里都有一句几乎一样的话:由于系统限定,请不要在输入输出中附带任何提示信息。这句话的分量比它看起来重得多。本地用 IDE 跑的时候,cout << "请输入金额:"这种提示语让人很舒服,但评测机是拿你的输出跟标准答案逐字符比对的,多一个汉字、多一个空行,结果直接从满分变成零分。
稳妥的输出习惯是:只输出题目要求的内容,每行末尾用endl或\n换行,不要在最后多打一个空行,也不要在两行结果之间插额外说明。写完之后把样例输入自己跑一遍,把输出复制到记事本里,打开「显示空格」对照样例逐字符看一眼。这个动作花不了一分钟,能挡掉相当一部分翻车。
4. 分支与循环:执行次数、条件顺序与等价转换
这一块是编程题的地基,也是客观题里计算量最大的部分。真题里连着的四道题(第六到第九题)全在考「这段代码到底执行了几次、输出了什么」,第十题则把循环用在质数判断上。核心能力只有一个:能在脑子里把循环跑一遍,并且跑对。
4.1 if / else if:命中一个就跳出整条链
第六题输入 21,21 % 3 == 0成立,输出「能被3整除」,然后整条else if链结束,后面两个分支都不会执行。答案是「代码第 4 行被执行」。这题的错误选项集中在「第 4 行和第 7 行都执行」,说明很多人把else if当成了两个独立的if。
if / else if / else是一条互斥链,从上往下依次判断,谁先成立就执行谁,执行完直接跳出。如果要让多个条件各自独立生效,就得写成连续的if,不加else。这两种写法在真题里都出现过,判断的关键是有没有else。
#include <iostream> using namespace std; int main() { int n = 21; if (n % 3 == 0) cout << "能被3整除" << endl; // 命中后跳出整条链 else if (n % 7 == 0) cout << "能被7整除" << endl; // 不会执行 else cout << "都不能" << endl; // 不会执行 return 0; }如果把三个分支改成三个独立的if,21 会同时满足「被 3 整除」和「被 7 整除」,两行都输出。考场上遇到这类题,先看有没有else,再看条件的先后顺序,最后才去算余数。
4.2 for 循环执行次数:两种数法互相验证
第七题for(int i=-10; i<10; i++)问循环体执行几次。第一种数法是「终点减起点」:从 -10 到 9,一共 20 个整数,执行 20 次。第二种数法是「列首尾」:-10、-9、…、0、…、9,数一遍也是 20。两种方法对上了才敢落笔,因为这类题错一个数就是全错。
常见的边界错误有三种:把<当成<=多算一次;从 0 开始时忘了 0 本身也算一次;跨过 0 的区间按正数习惯算成 10 次。判断题第十题「任何 for 循环都能转成等价 while」是对的,因为 for 的三个部分分别对应 while 的初始化、条件和迭代,位置可以完全拆开。
#include <iostream> using namespace std; int main() { for (int i = -10; i < 10; i++) // i 取 -10 到 9,共 20 个值 cout << i << " "; cout << endl; int j = -10; // 等价的 while 写法 while (j < 10) { cout << j << " "; j++; // 迭代语句挪到循环体末尾 } return 0; }两种写法输出完全一致。转换时唯一要留神的是continue:for 里的continue会跳到i++,while 里的continue会跳过循环体末尾的j++,一不小心就死循环。一级不考带continue的转换题,但知道这个坑,后面学循环控制会顺很多。
4.3 循环体里的筛选与累加:先翻译条件,再动手算
第八题的代码是遍历 0 到 9,满足i%3 && i%7就累加。把条件翻译成人话:不能被 3 整除,也不能被 7 整除。0 到 9 里符合的是 1、2、4、5、8,加起来是 20。四个选项里 0、7、18、20 都有人选,选 18 的通常是漏了 8 或者多算了 7。
第九题更绕:while(N)里先N -= 1再判断N%3 == 0。N 从 10 开始,减完是 9,输出9#;一路减到 3 输出3#;再减到 0 时,0 % 3 == 0成立,输出0#,然后while(0)退出。结果是9#6#3#0#。错选9#6#3#的人是把while(N)的判断时机记反了,以为 0 不会进循环,其实进循环的时候 N 还是 1。
#include <iostream> using namespace std; int main() { int total = 0; for (int i = 0; i < 10; i++) if (i % 3 && i % 7) // 非零即真:两个余数都不为 0 total += i; // 累加 1、2、4、5、8 cout << total << endl; // 20 return 0; }做这类题的统一动作是:在草稿纸上画一列「i 的值 / 条件是否成立 / 累加后的结果」,一边跑一边填。心算三五步还行,超过五步必错。
4.4 质数判断:循环里最经典的一段填空
第十题给出质数判断的骨架,横线要填的是N % i == 0。思路是用 1 到 N 之间的每个数去除 N,统计能整除的次数,次数恰好为 2(只有 1 和它本身)就是质数。填N % i得到的是余数,非零为真,逻辑正好反过来;填N / i == 0更离谱,除法结果是商,除非 N 小于 i 否则永远不为 0。
这段代码虽然效率不高(N 多大就循环多少次),但一级的数据规模很小,从 1 枚举到 N 完全够用。它同时也是「循环 + 分支 + 计数器」三件套的组合范例,编程题里的找因数几乎是它的孪生兄弟。
5. 易漏点清单:五个最容易在考场上翻车的地方
前面几章是按知识模块走的,这一章换个角度,按「错误类型」把整张卷子里最容易丢分的地方重新串一遍。每一条都按同一个套路写:先说什么现象,再说为什么会漏,最后给一个可以立刻执行的动作。
5.1 易漏点一:空格和换行藏在字符串里,眼睛看不见
真题第四题和第五题的差别只有一个空格,选项却围着它设了四个。这类题的共同特征是:cout里的字符串常量、printf的格式串,空白字符混在普通字符中间,扫一眼根本分辨不出来。
为什么会漏?因为人读代码是「跳读」的,大脑会自动把连续空白压缩成一个,读中文时这个习惯帮我们省了很多力气,读代码时就变成了系统性偏差。尤其是等号、冒号、逗号后面的空格,最容易被当成排版而不是内容。
怎么补?养成「逐字符核对」的动笔习惯:把输出内容抄在纸上,用竖线分隔每个字符,空格单独画一个小方格,然后跟选项一处一处对。抄一遍花三十秒,比反复看五遍管用。写程序时也有个笨办法,输出前先在心里念一遍「这里有没有空格」,念不出来的地方就加上显式分隔符。
5.2 易漏点二:值相等被当成类型相同
判断题第七题说 3.0 和 3 值相等所以存储空间相同,答案是错。类似的还有'0'和0、"0"和'0',值看着像,类型完全不同。
为什么会漏?因为数学课上3.0 = 3是常识,进了编程语境这层常识反而成了干扰。更深一层的原因是,很多人学变量时只记住了「变量能存数」,没建立「类型决定存储方式和运算规则」这个模型,于是所有数字在脑子里都是同一类东西。
怎么补?把类型当成变量的一部分来记:写int a的时候脑子里要出现「整数、4 字节、整除、取模可用」,写double b的时候出现「小数、8 字节、有精度误差」。练习方式很简单,遇到任何一个字面量,先问自己三句:它是什么类型、占多少空间、能不能参与取模。
5.3 易漏点三:循环边界差一,多数一次或少算一次
第七题从 -10 数到 9 是 20 次,很多人第一反应是 21 或者 10。第九题的while里 0 会不会进循环,也是同一类问题。
为什么会漏?因为 for 循环的三个部分里,条件用的是<还是<=只在开头出现一次,后面全靠记忆。再加上负数、0、跨零区间这些不常见的起点,平时的肌肉记忆直接失效。
怎么补?两个动作。第一,写循环前先把首项和末项写在注释里,比如// i 从 -10 到 9,写完再对照条件检查一遍。第二,用「小数据验证法」:把上界改成 3 手动跑一遍,看输出几个数,对上了再换回原值。这个方法在纸面考试里同样适用,在草稿纸上跑一个缩小版比硬算安全。
5.4 易漏点四:把输入函数和输出函数、提示函数搞混
判断题第三题说scanf()的参数是字符串字面量、功能是提示输入,答案是错。scanf是输入函数,第一个参数是格式串,不是提示语。第十二题问哪个语句能完成数据输入,四个选项里只有scanf是输入,printf和cout是输出,default是switch里的标签,跟输入输出毫无关系。
为什么会漏?因为初学阶段很多人是靠「背长相」记函数的,scanf和printf长得像、都是标准库函数、都带格式串,只差一个字母,用的时候凭感觉挑一个。cin和cout同理,in和out的区别在紧张时会被忽略。
怎么补?给每个函数配一句话的职责说明,写在笔记最显眼的位置:cin >>从键盘读、cout <<往屏幕写、scanf按格式读、printf按格式写。然后记一个判断口诀——「尖括号朝里的方向就是数据流动的方向」,>>流进来,<<流出去。
5.5 易漏点五:编程题输出多余提示语或多打空行
两道编程题的题干都明确要求不要附带提示信息,但每年仍然有人在这里丢分。表现有三种:输出前加一句「请输入」、两行结果之间多一个空行、最后多一个换行。
为什么会漏?因为本地调试时提示语让程序好用,而评测机的比对是逐字符的,人眼看上去「一样」的输出,在字节层面可能差好几个。更麻烦的是,这种错误自己测不出来,因为自己看自己的输出永远觉得对。
怎么补?交卷前做一次「纯净输出检查」:把所有cout和printf里跟题目要求无关的字符串全部删掉,只留数字和题目指定的符号;然后把样例输入跑一遍,复制输出,粘贴到能显示空白字符的编辑器里,跟样例输出逐行比对。这个习惯一旦养成,后面所有线上评测都能少踩很多坑。
6. 两道编程题拆解与考前复习清单
两道编程题的分值加起来占一半,但它们考的东西比客观题还基础。第一道是买书类问题:一本书 13 元,输入零用钱总额,输出最多能买几本、还剩多少钱。本质就是一次整除加一次取模,money / 13得本数,money % 13得余数,两行输出,顺序不能反。
#include <iostream> using namespace std; int main() { int money; cin >> money; // 零用钱总额 cout << money / 13 << endl; // 第一行:本数,整数除法自动向下取整 cout << money % 13 << endl; // 第二行:剩余的钱 return 0; }第二道是找因数:输入一个正整数 n,从小到大输出它的所有因数,每个一行。做法是从 1 枚举到 n,能整除就输出,天然有序,不需要额外排序。一级的数据规模很小,这个 O(n) 的写法完全够用,不用去想平方根优化。
#include <iostream> using namespace std; int main() { int n; cin >> n; for (int i = 1; i <= n; i++) { // 从 1 枚举到 n 本身 if (n % i == 0) // 余数为 0 说明 i 是因数 cout << i << endl; // 每行一个,顺序天然从小到大 } return 0; }这两道题合起来覆盖了一级编程题的全部套路:读入一个整数、用整除取模或循环处理、按格式输出。考前把这两段代码默写一遍,比刷十道选择题更稳。默写时重点检查三处:cin后面有没有写>>、循环条件是<还是<=、每行输出后面有没有换行。
复习清单按「能不能独立复现」来定标准,不按「看没看过」:
| 模块 | 自检动作 | 达标标准 |
|---|---|---|
| 运算符优先级 | 不查表写出2+3*4%5的结果 | 30 秒内答出 4 并说清步骤 |
| 取模与整除 | 写出取n个位、十位的表达式 | 一次写对n%10、n/10%10 |
| 类型转换 | 说出int x = 3.99的结果 | 答 3 并解释是截断不是四舍五入 |
| cout / printf | 写出带空格和不带空格的两种输出 | 两种写法都不看资料 |
| cin 读入 | 解释输入5+2后int变量的值 | 答 5 并说明+留在缓冲区 |
| 分支 | 判断if/else if链会执行几个分支 | 答一个,并说清互斥原因 |
| 循环次数 | 3 秒内算出for(i=-10;i<10;i++)的次数 | 答 20,能说出两种数法 |
| 编程题 | 默写买书和找因数两段代码 | 无提示、一次通过样例 |
清单里任何一项做不到,就回去翻对应的那一章,不要跳着补。
最后留一个我自己踩过的坑。早年做纸面题时,我总觉得输出题的答案「差不多就行」,空格多一个少一个无所谓,直到有一次模拟测里连续三道输出题全错,才发现问题不在会不会,而在核不核对。从那以后我每次写完输出语句,都会把样例输出贴在旁边,用尺子比着逐字符看一遍,包括行尾有没有多余空格。这个动作看起来傻,但它救回来的分数比任何技巧都多。希望帮到你。
本文还有配套的精品资源,点击获取