C++上机OJ高频考点:输入输出、字符串、排序与调试技巧

发布时间:2026/10/13 8:54:47
C++上机OJ高频考点:输入输出、字符串、排序与调试技巧
每年期末的上机考试总有人对着OJ提交页面怀疑人生本地跑得好好的一交上去就是编译错误或者答案错误最后盯着屏幕发十分钟呆。其实C上机OJ题翻来覆去就那么几类题型和考点相对固定。这份基于某邮电类高校信通专业2018年期末上机考试的题目汇总就是我从当年考场里一套一套整理出来的纯干货覆盖了输入输出处理、字符串操作、排序结构体、模拟题和简单的类设计等高频考点适合所有准备C上机考试的同学参考。考试环境是机房机器上的本地OJ系统编译器支持C11标准两小时完成全部题目。和笔试最大的区别在于OJ只看提交代码的最终运行结果过程分基本没有一道题要么满分要么零分。所以除了把题做对还得熟悉OJ的各种脾气——输入输出的细微差别、编译错误的英文提示、运行时错误的常见诱因这些才是上机考试真正拉开差距的地方。1. 2018年信通版上机考试全景概览1.1 考试环境与题型分布先说考试的基本盘。上机考试采用的是机房预装的本地评测系统提交后由服务器自动编译运行用测试用例比对输出结果只要有一个测试点不过这道题就一分没有。这一点和平时在IDE里写完随便跑一下完全不同必须严格按照题目要求的输入输出格式来写代码。当年这场考试的题目数量是7道时长两小时难度呈明显的梯度分布。前2到3题属于签到题考察最基本的输入输出和分支循环只要不犯低级错误就能拿分中间两到三题开始考察字符串处理、排序算法和简单模拟最后一题基本是压轴题涉及类与对象或文件流的综合应用用来控制高分段的区分度。很多同学容易忽略的是OJ评测的输入数据往往不是一组而是多组连续输入。题目里凡是写“输入包含多组测试数据”或者“输入以EOF结束”的代码就必须写成循环读取的格式。一旦漏掉这个循环结构评测程序只会拿第一组数据跑一次后面的测试点全部超时或者输出错误。1.2 六类必考题型速览根据2018年信通版题目的整体情况可以把考点归纳成六个大类。每类题型的出现频率和复习优先级我用表格整理出来了方便大家对照自查。题型类别出现频率难度核心考点基础输入输出必考1~2题低cin/cout或scanf/printf、多组输入、EOF判断、格式控制分支与循环必考1题低if-else嵌套、循环边界、递推累加字符串处理高频1~2题中字符数组或string类、子串查找、单词统计、大小写转换数组与函数高频1题中数组遍历、冒泡/选择排序、函数封装与传参结构体与排序高频1题中高结构体定义、sort自定义比较器、多关键字排序类与对象/文件流末题1题高类的封装、运算符重载、文件读写操作这个分布并不是某一年特有的近几年信通专业的上机考试基本沿用了同样的结构。也就是说如果把上面这张表里的每类题都准备到位哪怕压轴题没完全写出来前面几类题稳定拿分成绩也不会差到哪去。真正的备考策略不是去刷多少偏题怪题而是把这几类高频考点的代码模板记熟、敲熟。2. 必考点详解从输入输出到字符串处理2.1 输入输出格式——最容易被扣分的隐秘角落输入输出是每套OJ题的第一道关也是扣分重灾区。常见的问题不是不会写而是写得不够“精准”。先说多组输入。题目要求“输入包含多组测试数据每组占一行”时标准写法是#include bits/stdc.h using namespace std; int main() { int a, b; while (cin a b) { cout a b endl; } return 0; }这段代码的核心在于while (cin a b)当输入流到达文件末尾时cin的布尔值转为false循环自动结束。用scanf也一样写成while (scanf(%d %d, a, b) 2)或者判断! EOF。另一种常见格式是“第一行为一个整数N表示测试数据的组数接下来N行每行……”。这种题反而容易出错——只读一组就退出循环或者多读了N后面的空白字符。正确写法是int n; cin n; while (n--) { // 处理每组数据 }注意细节读完N之后如果下一行是字符串可能需要先getline吃掉残留的换行符。这是C上机最常见的坑之一后面会专门讲。然后是输出格式。OJ对输出的比对是逐字符匹配的哪怕多一个空格、少一个换行都会判错。题目要求“输出占一行每个数之间用一个空格隔开”时有人习惯性地在每次循环末尾都输出空格结果最后一个数后面多了一个空格WA答案错误没跑。控制分隔符的经典写法是for (int i 0; i n; i) { if (i) cout ; // 不是第一个数就补空格 cout arr[i]; }另外浮点数输出常常要求保留小数点后两位格式控制用fixed setprecision(2)头文件是iomanip。如果题目没明确要求保留小数就不要画蛇添足地加fixed否则输出格式可能不符。2.2 字符串处理——上机题的第一大“送分题”与“送命题”字符串处理类题目在信通版上机考试里几乎年年出现。以前的老教材还在用char[]字符数组但考试环境支持C11标准直接使用string类会省太多事。有一道典型的字符串题是这样的输入一行英文句子要求统计其中单词的个数单词之间以一个或多个空格分隔。这类题的常见错误是把“空格数加一”当成单词数遇到前后有空格或者多个连续空格的情况就踩坑。正确的思路是遍历整个字符串用状态切换的方式计数#include bits/stdc.h using namespace std; int main() { string s; getline(cin, s); // 注意cin s 读不了空格 int cnt 0; bool inWord false; for (char c : s) { if (c ) { inWord false; } else if (!inWord) { inWord true; cnt; } } cout cnt endl; return 0; }这段代码用getline(cin, s)读入整行文本cin s遇到空格就会停下来这是初学者最容易踩的坑。inWord这个布尔变量用来标记当前是否处于一个单词内部只有从非单词状态进入单词状态的瞬间才计数。这样无论句子前后有没有空格、中间有多少连续空格统计结果都是正确的。另一个高频考点是字符串反转或大小写转换。反转单个字符串可以原地交换字符也可以用reverse(s.begin(), s.end())一行搞定。但如果题目要求“反转每个单词但不改变单词之间的顺序”就要先按空格分割再分别反转每个子串。大小写转换有一个很实用的ASCII码技巧大写字母转小写只需要加上32小写转大写减去32。判断时用c a c z。不过更稳妥的写法是使用cctype库里的tolower()和toupper()函数这两个函数会自动处理字符范围不会因为字母边界问题而出错。字符串截取和查找也是常客。find()函数能找到子串第一次出现的位置没找到时返回string::npos。这里必须注意判断条件是if (pos string::npos)而不是if (pos -1)。虽然npos的值通常就是-1的无符号版本但直接比较-1在某些编译器下会给出警告考试时看到警告别慌但最好还是用标准写法。还有一个容易被忽略的问题getline读取完一行后如果后面再读整数或者反过来读完整数再读字符串中间残留的换行符会导致读入错位。解决办法是读完整数后用cin.ignore()清掉换行符或者统一用getline读入再手动解析。考试时如果发现自己输出完全对不上先检查是不是输入读取就发生了错位。3. 核心算法考点排序、模拟与结构体应用3.1 排序问题——不只是sort函数这么简单排序题是信通版上机考试的常青树几乎每年都有一道。考察重点不是排序算法本身——毕竟std::sort一分钟就写完了——而是排序规则的定制能力。比如经典的成绩排序题输入若干学生的姓名和成绩要求按成绩从高到低排序成绩相同则按姓名字典序从小到大排序。用结构体加sort自定义比较器代码非常简洁#include bits/stdc.h using namespace std; struct Student { string name; int score; }; bool cmp(const Student a, const Student b) { if (a.score ! b.score) return a.score b.score; // 成绩降序 return a.name b.name; // 姓名升序 } int main() { int n; cin n; vectorStudent stu(n); for (int i 0; i n; i) { cin stu[i].name stu[i].score; } sort(stu.begin(), stu.end(), cmp); for (auto s : stu) { cout s.name s.score endl; } return 0; }这里有两个细节需要强调。第一个比较器cmp必须返回严格的“严格弱序”关系——也就是a排在b前面时返回true。如果排序规则里存在逻辑漏洞比如比较器写出了return a.score b.scoresort的行为就是未定义的轻则排序结果不对重则直接运行时报错。第二个在比较器参数里用const Student引用传参避免结构体拷贝的开销。虽然这里结构体体积小影响不大但养成这个习惯在后面的OJ题里会受益。如果题目要求不使用sort而是手写排序算法最稳妥的是选择排序或冒泡排序。这些老算法本身效率比不上快排但对于OJ题目的数据规模通常n不超过1000完全够用。写手写排序时注意交换操作的边界别让数组越界。3.2 模拟类题目——把题目翻译成代码的关键套路所谓模拟题就是题目描述某一个过程比如“约瑟夫环”“日期计算”或“进制转换”要求按步骤把过程用代码实现出来。这类题目本身不涉及高深的算法知识点但非常考验把自然语言翻译成代码的能力。日期计算题是模拟题中的经典款。常见问法是给定某年某月某日输出这是这一年的第几天。思路是先把每个月的天数存进数组然后逐月累加。注意闰年的判断(year % 4 0 year % 100 ! 0) || year % 400 0。这个判断条件几乎每年都有人写错漏掉year % 100 ! 0或者把||和的优先级搞混。约瑟夫环这类循环模拟题的通用解法是维护一个数组标记哪些位置的人已经出列用一个指针循环移动。注意当指针到达数组末尾时要回到开头也就是取模操作pos (pos 1) % n。这个取模操作也是很多人的混乱点循环走指针是先加一再取模还是先取模再加一多调试几组小数据就清楚了。模拟题的另一个核心套路是把复杂问题拆成多个小函数。比如进制转换题可以先写一个“将十进制数转换成k进制字符串”的函数再写一个“把k进制字符串转换成十进制数”的函数主程序里只负责调用。这种做法既降低写错概率也方便局部调试。考试时不要嫌弃函数调用的代码量分而治之才是上机题的正确打开方式。3.3 结构体与文件流——贴近真实开发的考点结构体在信通专业的上机题里往往不是单独出现的而是配合排序或链表操作一起考察。前面的成绩排序已经展示了结构体的基本用法这里补充一个关键但容易被忽略的知识点结构体变量的整体赋值。两个相同类型的结构体变量可以直接互相赋值比如stu[1] stu[0];默认是浅拷贝。对于C string类型的成员这种拷贝是安全的但如果结构体里有指针成员浅拷贝就会出现两个变量指向同一块内存的问题。信通的专业课后续会学数据结构上机题里通常不会考到深拷贝那么深但了解这个区别能帮你排查奇怪的运行结果。文件流操作是压轴题的常客。典型题目是从文本文件中读取若干行数据经过处理后写入另一个文件。核心代码框架如下#include bits/stdc.h using namespace std; int main() { ifstream fin(input.txt); ofstream fout(output.txt); if (!fin.is_open()) { return 1; // 文件打开失败 } string line; while (getline(fin, line)) { // 处理每一行 fout line endl; } fin.close(); fout.close(); return 0; }这里最容易踩的坑是文件路径。考试时题目通常会说明数据文件名称但不会说路径默认就在当前目录下。如果代码里写了绝对路径换一台机器评测就找不到了。所以一律用相对路径也就是文件名称本身不要自作聪明地加目录。另外写完记得close()尤其是有多个文件操作时文件句柄不释放会导致后续操作异常。还有一个小众但考过的点中文文本的读取。过去的C风格字符数组处理UTF-8中文很容易乱码用string加getline来做按行处理情况会好很多。如果确实需要判断中文字符的某个字节记住UTF-8中每个汉字占3个字节实测这么判断一般没问题。4. 调试技巧与考场避坑指南4.1 万能调试三板斧上机考试没有断点调试器至少说很多同学在考场上根本不会用IDE的调试功能。这种情况下最快的排错手段就是在代码里插入输出语句。第一板斧关键位置打印中间变量。比如排序后打印一遍数组检查排序规则是否正确模拟题的每个大循环末尾打印当前状态看数据是不是按预期在变化。考场上不要舍不得删调试输出——确认逻辑正确后删掉重交一次就行。不过要注意OI赛制和部分OJ系统会屏蔽额外的输出内容所以提交前一定记得清理干净。第二板斧构造小规模测试数据。OJ给的测试样例通常只有一组覆盖不全。自己构造几组边界数据来测没有输入的情况、只有一个元素的情况、重复元素的情况、字符串为空的极端情况。很多时候答案错误都是因为没考虑边界条件。第三板斧利用返回值判断错误类型。程序运行时直接崩溃OJ会给出RE运行时错误。RE的原因不外乎数组越界、除零、栈溢出、空指针。用二分自查的方式在代码里加return 0;来缩小崩溃范围——先在最开头加看是否还RE不RE再往后挪最终定位到崩溃点。这个办法虽然笨但非常有效实测能在五分钟内定位绝大多数RE问题。4.2 典型编译错误与运行错误速查编译错误是考场上的常见拦路虎。很多人看到大段英文报错就慌了其实常见的就那几种记住关键词就能快速定位。undefined reference to main链接器找不到主函数通常是代码里漏写了int main()或者把main拼成了mian。expected ; before ...某个语句末尾漏了分号或者上一行结构不完整。xxx was not declared in this scope变量或函数没有声明可能是拼写错了、忘记声明变量、或者引用的头文件没包含。运行错误也有规律。最常见的是段错误Segmentation fault本质是访问了非法内存地址通常由数组越界或访问空指针导致。另外就是浮点异常Floating point exception其实是整数除零想想代码里有没有可能让分母或者取模的模数变成0。编译警告里还有一条很坑比较有符号和无符号整数时会提示comparison between signed and unsigned integer expressions。比如string s; ... s.length()返回无符号类型拿它和int i比较就可能触发这个问题。上机考试里这种警告一般不影响评测但如果开了-Werror选项警告直接升级为错误编译就不通过了。稳妥做法是把下标变量声明成size_t类型或者每次使用时强转(int)s.length()。5. 常见问题排查与备考建议5.1 上机考试典型问题速查表我把考场上最常见的现象、可能的原因和对应解法整理成了一张速查表希望大家上机前花两分钟扫一眼遇到问题能直接对照定位。现象可能原因排查思路本地运行正确OJ上显示答案错误多组输入没循环读输出格式多了空格或换行变量没初始化检查while循环结构用最简单的样例逐字符比对输出编译错误expected initializer before ...结构体或类定义末尾漏了分号检查结构体}后面是否有;编译错误stray \243 in program代码里混入了中文字符或全角标点检查注释和字符串里的中文引号建议代码注释也写成英文运行超时TLE算法效率太低死循环输入没读完检查循环条件是否更新n是否递增排序题确认没有写死循环运行错误RE数组越界除零递归栈溢出检查所有数组下标范围补边界判断别让分母出现0本地输出中文乱码编码格式不兼容上机题的字符串一般没有中文字符尽量用英文处理5.2 考前一周的复习建议如果离考试还有一周最高效的复习方案不是狂刷新题而是做三件事。第一把本目录里的每一类题的模板代码亲手敲一遍。手和眼是不一样的看得懂不等于敲得对。尤其是sort自定义比较器、getline消费换行符、文件流读写这几段代码考场上是没有时间现查语法规则的。敲到条件反射才算真正掌握。第二狠抠输入输出的边界条件。随便找一些字符串题故意在数据前后加空格、中间加连续空位测试自己的代码是不是还能稳定运行。上机OJ的评测数据里边界和极端情况往往才是真正拉开分数的位置。这个阶段多花一个小时考场上就少一次无谓的WA。第三做一次限时模拟。在OJ平台上随便找一套题严格按两小时标准计时中间不翻教材不查资料完全模拟考场状态。重点不是做对多少题而是训练时间分配签到题15分钟内解决中等题一道不超过半小时最后一题如果卡了20分钟还没思路果断放弃写前面的检查。最后再分享一个考场小技巧交卷前留五分钟从头到尾检查一遍代码。主要检查有没有多余的调试输出、数组开得够不够大、有没有把题目的文件名拼错。这五分钟通常能救回好几分。希望这份基于2018年信通版考试整理出来的干货能帮你在上机考场上少踩坑、多拿分。祝顺利。