C语言指针与结构体编程题通关指南:在线测试避坑要点

发布时间:2026/10/13 21:16:42
C语言指针与结构体编程题通关指南:在线测试避坑要点
最近好几个同学在群里说某大型在线课程平台的某高校《C语言程序设计》第10周在线测试有点崩有的是编译过不去有的是本地跑得好好的但提交就是WA。刚好我前阵子也把这门课的十周编程题全部刷了一遍第10周这道坎确实值得专门写一篇经验帖尤其是第一次接触指针和结构体的同学很容易在判题系统里栽跟头。这篇内容我会从“这周到底考什么”开始拆然后是判题系统怎么看你的代码再到三到四类最常出现的题目怎么下手最后是我自己踩过的一些坑和考前时间分配建议。不论你是零基础刚学到指针还是复习冲刺准备补考这篇都能给你一些可操作的东西。1. 第10周编程题到底考什么核心考点与出题套路1.1 周次定位指针和结构体正式进场多数C语言课程的进度安排里第10周正好是“指针初步、字符串数组、结构体”这些内容集中发力的阶段。前面的周次还在讲顺序、循环、数组做题时可以靠“无脑遍历”“下标硬算”来对付。到了第10周题目开始强制要求你用指针去操作内存或者把多属性数据打包成结构体排序。我个人的感受是第10周的在线测试就是学校在提醒你——你不可能永远用int a[100]这一招走天下了。在线测试里最常见的三道题都是围绕以下知识点展开字符数组与字符串函数的使用比如统计字母数字个数、子串查找、字符串逆序指针作为函数参数实现数据的“真正修改”比如交换两个变量、查找数组最大值的下标结构体数组的定义、输入输出、按成绩排序。有的年份会加一道“链表创建与遍历”作为压轴但第10周通常不会太难毕竟链表一般放在第11周到第12周。如果你们的课程进度偏快出现了链表也不要慌本质还是结构体加指针后面我会说一嘴。1.2 在线测试题目与人工评分的本质区别在线测试和平时做课后作业最大的不同在于它没有“人情分”。人工批改会看你的思路步骤对就给一部分分数在线测试只看最终的输入输出匹配结果。哪怕你逻辑完全正确输出中间多了一个空格系统里可能就是Wrong Answer或多一个提示性文字也会直接判错。我在刷第10周题目时特意观察了系统的反馈常见的结果有这么几类判题结果含义常见原因AC正确输出完全符合要求WA结果错误算法逻辑有问题或输出格式不匹配PE格式错误空格、换行位置不对老版判题系统会区分RE运行时错误数组越界、指针访问非法内存、除以零TLE超时算法效率太低第10周一般很少出现CE编译错误语法错误、缺头文件、函数声明位置不对很多同学看到 WA 就以为是自己思路错了其实很大概率是输出格式问题。比如要求每个结果占一行你却在结果后面加了一句“答案是”哪怕内容对了照样WA。这个细节我会在第2章专门展开。2. 在线测试的判题机制与应对策略2.1 黑盒测试只关心输入和输出在线测试的基本原理是系统编译你的源码然后运行你的程序把一组组测试数据通过标准输入stdin喂给你的程序再抓取你的标准输出stdout和标准答案作逐字符比对。因此你的程序必须做到“老老实实读入规规矩矩输出”。不要写任何多余的交互提示比如“请输入学生数量”这种话。本地调试时人眼看到提示很方便但判题系统不需要它会把这些字符也当成你的输出。第10周的题目输入格式一般是第一行是一个整数 n表示后面有几组数据接着是 n 行每行由若干数据项组成比如姓名和成绩。这种格式很常见。我建议第10周的练习题统一采用下面的读取套路int n; scanf(%d, n); // 如果后面要读字符串注意吸收掉第一行末尾的换行符 getchar();这里有个容易被忽略的点当你的 scanf 读完一个整数后输入缓冲区里还残留着换行符 “\n”。这时候直接用 gets 或 fgets 读字符串会读到空串。最简单的方法是在两者之间加一个 getchar()把这个换行符吃掉。这个问题在在线测试里出现的频率极高尤其是涉及结构体数组时一上来就少读一条数据。2.2 输出格式的“隐形杀手”怎么避免判题系统比对输出时空格、换行、tab 都会被当成字符参与比较。常见的要求有这么几种需要严格区分每个输出结果占一行每行内部数据项之间用一个空格分隔行尾可以有换行符但不能有额外空格题目要求的输出样例里如果是“姓名 空格 成绩”你就不能输出成“姓名 逗号 成绩”某些题目要求每个输出结束后空一行这需要仔细看题目描述。我有一次就在“每个结果之间空一行”上栽了。题目明确说“两个样例输出之间加一个空行”我忽略了提交一直 WA。后来我把输出改成先输出第一个结果不换行不对是每个结果输出后加一个额外的空行。给大家一个通用模板可以少踩很多坑// 其中 flag 表示是否已经输出过一行 int flag 0; for (int i 0; i n; i) { // 计算当前结果 if (flag) printf(\n); printf(%d\n, result); flag 1; }如果是每个数据项之间空格最后一个数据后面不要加空格可以用判断索引的方式控制for (int j 0; j m; j) { if (j 0) printf( ); printf(%d, arr[j]); } printf(\n);这种写法能保证行尾没有多余空格是应对空格敏感性判题的好习惯。平时在本地 IDE 里怎么输出都无所谓因为人眼看得开但机器无情多一个空格就是大叉。3. 高频题型拆解与代码实现要点3.1 字符串处理题字符统计、逆序与子串第10周最爱出的一类题目是给你一段字符串让你统计其中字母、数字或其他字符出现的次数。看起来简单但考察了字符串结束符 ‘\0’ 的意识和字符函数的使用。比如这道典型的题输入一行字符分别统计英文字母、数字、空格和其他字符的个数。#include stdio.h #include ctype.h int main() { char s[200]; int letters 0, digits 0, spaces 0, others 0; // 注意gets 在新版 C 标准中已被废弃建议用 fgets fgets(s, sizeof(s), stdin); for (int i 0; s[i] ! \0; i) { if (isalpha((unsigned char)s[i])) letters; else if (isdigit((unsigned char)s[i])) digits; else if (s[i] ) spaces; else if (s[i] ! \n) // fgets 会读入换行 others; } printf(letters%d, digits%d, spaces%d, others%d\n, letters, digits, spaces); return 0; }这里有几个细节为什么用 fgets 而不是 gets因为 gets 不检查缓冲区边界在判定系统里可能造成缓冲溢出而且新版 C 标准已经移除 gets。fgets 会连换行符一起读进来所以统计 others 时要把末尾的换行符排除掉。为什么用 isalpha、isdigit 而不是自己写判断条件因为这些函数可读性强不易出错。注意要包含 ctype.h并且把 s[i] 强转成 unsigned char避免高比特位字符传给字符函数时出现未定义行为。输入只有一行还好办如果题目说输入可能有多行直到 EOF那就得用 while (fgets(...) ! NULL) 的循环。另一种常见的字符串题是逆序输出。题目要求输入一个字符串将单词顺序或字符顺序反转。如果是字符顺序反转比较简单#include stdio.h #include string.h void reverse(char *s) { int len strlen(s); // 注意如果 fgets 读入包括换行需去掉换行 if (len 0 s[len - 1] \n) { s[--len] \0; } for (int i 0, j len - 1; i j; i, j--) { char temp s[i]; s[i] s[j]; s[j] temp; } } int main() { char s[100]; fgets(s, sizeof(s), stdin); reverse(s); printf(%s\n, s); return 0; }如果是“将句子中的单词顺序翻转每个单词内部不变”那就先分隔单词再逆序输出可以用二维字符数组存每个单词也可以找空格位置完成。这类题在第10周出现频率也高建议提前练习 strtok 函数char *token strtok(str, \n);不过 strtok 会临时修改原字符串在线测试时容易遇到边界问题如果你不熟悉它可以自己用循环分隔单词更稳。3.2 指针应用基础交换、查找与动态内存分配第10周必然有一道题在检验你对指针传参的理解。最经典的莫过于“用指针实现两个数交换”。很多新手写下面这种代码觉得很完美void swap(int a, int b) { int temp a; a b; b temp; }然后在 main 里调用发现 a、b 根本没换。原因很简单C 语言函数传参是值传递swap 函数里交换的是形参的副本不是实参本身。正确写法是指针void swap(int *a, int *b) { int temp *a; *a *b; *b temp; }调用时用swap(a, b)把变量地址传进去。这个知识点看似基础但第10周的在线测试特别爱把它包装成“输入三个数按从小到大排序输出”之类的题如果你用交换函数时写错了形参类型轻则结果不对重则编译报错。指针题还有一种常见考法利用函数返回数组的最大值和最小值。因为 C 语言不允许函数直接返回数组但可以通过指针参数“带出”多个结果。比如void minmax(int arr[], int n, int *max, int *min) { *max arr[0]; *min arr[0]; for (int i 1; i n; i) { if (arr[i] *max) *max arr[i]; if (arr[i] *min) *min arr[i]; } }这种模式的解题思路是要输出的结果利用指针参数在外部接收函数本身可以返回 void 或返回错误码。在在线测试里题目的函数原型往往已经给定你需要严格按照原型来实现不能自己改函数名或参数类型否则就算本地测试通过提交时也会 CE。至于动态内存分配第10周不一定会出现但如果你看到“输入 n 个学生信息n 不固定但 n 可能很大”这样的条件就要想到用 malloc 创建结构体数组struct student *stu (struct student *)malloc(n * sizeof(struct student)); if (stu NULL) { printf(Memory allocation failed!\n); return -1; }记得用完后 free(stu)在线测试判题时会看内存使用情况虽然偶尔不 free 也能过但养成 free 的习惯对后续学链表很有帮助。3.3 结构体数组成绩排序是必练题型第10周的结构体题几乎可以总结成一条公式定义结构体 输入 n 条记录 按某字段排序 输出。最常见的是学生信息表学号、姓名、成绩按成绩降序输出。先看结构体和比较函数怎么搭struct student { char name[50]; int score; };如果要求按成绩从高到低排序可以用标准库的 qsort也可以自己写冒泡排序。qsort 的用法很多新手不太熟我来写一套可以直接抄的模板#include stdio.h #include stdlib.h #include string.h struct student { char name[50]; int score; }; int cmp(const void *a, const void *b) { // 按成绩降序 if (((struct student *)a)-score ! ((struct student *)b)-score) return ((struct student *)b)-score - ((struct student *)a)-score; // 如果成绩相同按姓名字典序升序 return strcmp(((struct student *)a)-name, ((struct student *)b)-name); } int main() { int n; scanf(%d, n); getchar(); // 吃掉换行 struct student list[100]; for (int i 0; i n; i) { // 每行输入姓名 成绩 scanf(%s %d, list[i].name, list[i].score); } qsort(list, n, sizeof(struct student), cmp); for (int i 0; i n; i) { printf(%s %d\n, list[i].name, list[i].score); } return 0; }为什么比较函数里要强转成 struct student 指针因为 qsort 的参数是 void 指针必须显式类型转换才能访问字段。比较函数的返回值规则是第一个参数排在前面时返回负数排在后面时返回正数相等返回0。所以降序排序时用 b-score - a-score升序则是 a-score - b-score。自己写冒泡排序也不是不行第10周数据量小几十条记录排序完全够用。但 qsort 是标准库函数代码更简洁而且不容易写错交换逻辑。如果你以后参加更严苛的在线测试qsort 几乎每场都要用现在学会可以说一劳永逸。结构体输入时还有一个坑如果每行数据是“姓名 空格 成绩”用 scanf(%s %d) 就可以但如果姓名里本身可能含有空格就不能用 %s得用 fgets 分段处理。第10周一般不会要求处理带空格的姓名但如果有记住一点先 fgets 一整行再按空格或逗号拆分。4. 常见问题与排查技巧实录4.1 本地运行正常提交却 WA 或 RE怎么查我总结了一个“三步排查法”每次提交前按这个顺序过一遍能避免至少一半的无谓提交。第一步检查输出格式。把所有除必要输出以外的调试信息全部删掉。再看题目的输出样例是否包含行尾空格、空行。最简单的办法是把输出结果重定向到文本文件然后用编辑器显示所有字符看看有没有多余空格、换行顺序不对。./program input.txt output.txt在 Windows 下可以用命令或者在你的 IDE 里直接运行后检查控制台。把 output.txt 和标准答案对比用比较工具或肉眼检查。这一步能解决大多数 WA 中“格式不对”的问题。第二步检查数组边界。第10周题目里输入规模通常不会太大但如果你定义了char s[100]输入却超过100个字符fgets 会截断后面的字符被留在缓冲区可能导致数据错乱。要么把数组开大一点比如char s[1024]要么用动态内存。我习惯给字符数组和结构体数组留出两倍余量比如题目限制 n≤100我就开struct student stu[200]。为什么不能盲目开超大数组有些在线测试环境有内存限制但第10周一般是初学者题目内存限制很宽松开 10000 的数组也不会爆。相反开小了会直接 RE损失更大。第三步检查指针和内存。如果你用了指针问自己指针有没有初始化是否指向了合法的内存是否访问了越界下标写一个简单的堆内存图辅助判断。比如定义char *p;后直接gets(p);这样肯定崩溃因为 p 没有指向一块合法的内存。应该用数组或 malloc 分配空间。4.2 编译错误的常见原因与解决方法在线测试系统给出的编译错误信息通常比较简单很多同学一看到 CE 就懵。其实第10周 CE 就这几种原因头文件缺失。用到了 strlen 但不写#include string.h用到了 qsort 但不写#include stdlib.h。这类错误最容易查。函数声明顺序问题。如果你在 main 函数后面定义某个函数并且 main 中调用了它但前面没有声明编译器会隐式声明报冲突错误。解决方法是把函数定义放在 main 之前或者在 main 前面加函数原型声明。结构体类型定义位置。定义结构体 type 时如果没写;结尾或者把 struct student 定义在 main 内部而排序函数在外面用到了那就作用域不对。语法错误如scanf(%d, n);忘了取地址符 。这个在第10周非常常见因为前面常用 scanf 读字符数组字符数组名本身就是地址于是大家容易形成惯性读整数也漏 。对字符串使用 比较。想比较两个字符串内容是否相等不能写if (s abc)应该用 strcmp。我的经验是本地用 GCC 编译时开启警告选项-Wall -Wextra所有警告都尽量修正。很多 CE 在本地编译器里会直接报错但有些编译器警告不当作错误而在线测试的环境比较严格稍微不规范就可能 CE。提交前用“查看编译输出”功能看一下有没有 warning不要忽略任何警告。4.3 边界条件空串、单个字符、最大数据量在线测试的测试点至少包含四类样例数据、特殊边界、最小值、最大值。第10周常考的边界条件我列一个速查表场景典型错误正确做法输入字符串为空循环条件直接访问 s[0]先判断长度是否为0或用for (i 0; s[i]; i)能自动跳过只有1个字符逆序后还是它但容易越界检查交换循环的起止条件成绩全相同排序后顺序不稳定用 qsort 加第二关键字比如学号升序n0直接返回0不能输出错误信息判断 if (n 0) return 0;字符串长度等于数组容量没有位置放 \0数组大小至少为长度1姓名末位换行未去除输出时字符串后面带换行用if (s[len-1]\n) s[len-1]\0;一个提升通过率的技巧提交前自己额外构造几组特殊数据肉眼模拟判题系统。比如输入“0个学生” 看程序是否直接结束输入“成绩相同”看排序是否稳定输入“aaaaaa”看统计数字输入“Hello, World!”看空格和其他字符统计。这些边界测试数据不需要上交只是为了确认程序健壮性。5. 考试实战经验与时间管理5.1 拿到题目先做什么第10周在线测试通常限时90分钟到180分钟不等题量3到5道。我最推荐的做题顺序是先读所有题目再按“简单到难”排序依次编码调试。读题时重点圈出输入格式、输出格式、数据范围这三个部分。很多时候WA 就是因为没看清“每个结果占一行”和“数字之间用空格分隔”。第10周的题面一般不长耐心读完两遍不要急着敲代码。我给自己定的时间分配是第1到2题属于基础分大概每题15-20分钟第3题如果有结构体排序给25-30分钟第4题如果是指针变量操作给15分钟第5题如果涉及链表给20分钟。总的原则是先保证把简单题的 AC 拿到手不要卡在一道题上超过30分钟。在线测试不是竞赛多得一分是一分。5.2 提交策略与本地自测清单很多同学喜欢写完一次性提交错了再改白白消耗提交次数。我更推荐“小步快跑”的做法主函数框架写好先编译确认没有语法错误再把样例输入跑通最后提交。下面的自查清单我打印出来贴在显示器旁边每次提交前逐条打勾[ ] 代码中是否有测试用 printf 没有删除[ ] scanf 是否漏掉了变量前面的 [ ] 字符串数组大小是否足够容纳输入加 \0[ ] 输入整数后是否处理换行符导致字符串读入失败[ ] 结构体成员是通过指针访问还是直接访问写法是否正确[ ] 排序比较函数是否返回 int 而不是 bool[ ] 输出是否严格匹配样例的空格和换行[ ] 是否考虑了 n0 或单元素数组[ ] 如果使用了 malloc是否在分支返回前 free这套清单在平时练习时也用慢慢就会成为肌肉记忆。到了真正考试节省下来的时间就是分数。5.3 几种典型时间损耗现在就可以提前避免一是数组越界导致的 RE排查时间很长。预防方法很简单凡是数组下标和长度有关的循环先判断 i 的范围。在写 for 循环时统一用i n不要用i n-1这样更容易检查。二是忘记处理 fgets 的换行符导致判断字符串内容时多出一个未知字符。可以写一个小函数void strip_newline(char *s) { int len strlen(s); if (len 0 s[len - 1] \n) s[len - 1] \0; }每次用 fgets 读入字符串后调用一次能避免一半的字符串比较怪问题。三是不熟悉 qsort 的比较函数现场卡壳。建议在第10周之前就在本地写好一个可复用的“结构体成绩排序”模板比较函数复制粘贴改字段名即可。说到底第10周在线测试并不玄幻它只是逼着你把前几周的语法知识真正落到能运行的代码上。我个人的体会是这一周你踩过的每一个 RE 和 WA都是在给后面的链表和文件操作排雷。指针没搞懂的话第12周的链表会让你更崩溃。现在多花半小时把结构体和 qsort 练熟后面几周真的能轻松不少。最后再分享一个小技巧如果提交后错了不要反复盲目提交。先把错误输出和你的输出用文本比对文件一字一字看。很多时候你离 AC 就差一个空格或者说就差把调试用的那句 “result is” 删掉。谨慎、细致就是第10周在线测试最需要的能力。