一、 不能编译或能编译但运行异常可以结合printf语句打印缩小范围再设置断点逐行跟踪变量的值是否是预期值重点关注是否是内存分配不够溢出或者是一些逻辑问题二、能编译能运行但提交结果部分正确部分错误这种一般不好直接调试但可从以下方向进行排查1. 先核对 “输入输出格式”最容易忽略的坑如行末尾、行前是否有空格间隔是一个还是两个空格什么时候换行输出格式是否需要前导0最容易被忽略的点一定要注意不是什么时候都是%d输出该用%04d输出时就要用另外还有一个点就是一般最后一行输出其实不需要换行符如果加了换行符需要考虑像这输出格式如下 5 10000 23333 44444 55555 88888 而代码写成 printf(%d\n, len); qsort(res, len, sizeof(int), compare); int space 0; for (int i 0; i len; i) { if (!space) { printf(%05d, res[i]); space 1; } else { printf( %05d, res[i]); } } printf(\n); 可能会导致当len0时此时不应该有第二行输出但仍会多输出一个换行符。 所以最保险的办法是不加这个换行符不影响最终结果注释掉即可2. 构造 “测试用例” 覆盖全场景尤其注意题目中的边界条件检查3. 检查 “数据类型” 和 “计算精度注意字符串内存是否溢出, int型数据是否溢出等4. 逻辑错误重点关注初始化循环分支三类逻辑是否正确三、可以边输入边判断并输出不影响格式问题四、自己在PTA做题过程中遇到的一些借助ai才排查到的问题例子1.char str[81]; fgets(str, 80, stdin); //这里的80应该改为81是总长度! str[strcspn(str, \n)] \0;应改为char str[81]; fgets(str, 81, stdin); //是总长度! str[strcspn(str, \n)] \0;fgets会自动在末尾加\0所以这里的参数是包含\0在内的总长度如果写成则只最多只会从stdin中读79个字符不符合题目最多输入80个字符的意思2.对于超大整数如果超过int型范围换long long(64位)去接收scanf的输入格式和printf打印的占位符都是lld3. double类型如果要求打印整数需要先用(int)强转再用%d形式打印直接打印%d不能匹配double类型 4. 要求输出前导0时如4要求输出04用%02d占位符表示其中0表示前导02表示输出的总位数如04是两位%.05d表示输出总位数为5为不够的左侧补05.输出指定长度的字符串用%.*s格式其中占位符.*表示输出字符串的长度可以截断字符串转int用sscanf(char*,%d, n)特别注意传的是char*是地址可以是字符数组名3, 但是不能是s[3]这传入的是一个字符编译器会把这个字符的ascii码值当成内存地址去访问引起异常若题目说该数字的位数不超过1000位这种最好多定义几个字节比如char str[1005]又犯一次错 题目说不超过 100000 个字符的串我char s2[100000];// 这里出了问题不超过100000个字符那么加上\0也得有100001个了溢出 //应该换成就通过了 char s2[100005];6.题目要求四舍五入时用math.h里的一个round(double)函数自动对小数作四舍五入round(7.56)直接返回 8因为它只做 “保留 0 位小数” 的四舍五入若要保留 N 位小数必须通过 “×10^N → round () → ÷10^N” 的缩放逻辑实现round()对 0.5 的处理是 “向远离 0 的方向舍入”正数进 1负数退 17.关于只对一个数组中的有效元素找出最大最小值时如果初始化最大、最小的索引这样写int max_index 0; int min_index 0;如果索引0不是有效的那么需要先记录下第一个有效元素的索引first_effctive再初始化为int max first_effctive;int min first_effctive;8.int n; scanf(%d, n); getchar(); //吃掉换行符因为后面要用fgets读取 char** str (char**)malloc(sizeof(char*) * n); for (int i 0; i n; i) { str[i] (char*)malloc(sizeof(char) * 20); fgets(str[i], 17, stdin); str[i][strcspn(str[i], \n)] \0; }这里因为输入的字符串是一行包含空格所以用fgets去读但是之前的n用scanf读取后在缓冲区留下了换行符导致fgets会读取留下的这个换行符而错误所以一定要用getchar()吃掉上一步scanf留下的换行符保证后面的fgets正确读取 另外用scanf的%c格式读取时一定一定要小心它不会跳过换行符等会读取任意字符如果缓冲区输入有留下换行符是我们不期望读取的字符需要先getchar()吃掉处理9.double类型数据读取时一定用%lf格式去读才能读到复数乘法模值相乘和幅度角值可以直接相加得到的就是相乘后的模和幅度角即模相乘幅度相加三角函数sin可以直接用参数和返回都是doublefabs(double) 用于返回浮点数double类型数的绝对值1e-4表示0.000110. 打印%需要转义%本身表示占位符开始所以打印%需要用专门的%%来打印11.sscanf处理包含非法字符的字符串比如12a3时并不会直接返回失败而是会 “尽可能读取合法部分”这就导致你无法准确判断输入是否为纯数字比如读取为12到a不匹配时就停下。返回2成功读取的数量所以在转换之前需要先遍历字符判断是否合法12. p\0 与 p NULL 的区别*p \0和p NULL是完全独立的两个状态没有任何必然联系我用最直白的方式解释p NULL表示指针变量p本身的值是 0空地址它没有指向任何有效的内存空间此时访问*p会直接导致程序崩溃*p \0表示指针p指向的有效内存地址中存储的字符是字符串结束符此时p本身是一个有效的非空指针13.字符串比较一定要用strcmp即使是字符串与单个字符串#这种比较也要用strcmp在处理输入以读取到#为结束标志时由于#不算输入所以注意这时count的统计不能当题目直说输入字符串以回车结束时需要考虑输入可能有空格等空白字符所以用fgets读取更好用scanf读取会漏掉要求输入的空白字符14.C 语言不会在运行时检查数组索引是否越界访问arr[n]不会直接崩溃而是会读取「数组内存后方的相邻垃圾内存」内存类型越界,栈数组在main函数里面静态定义的数组大概率段错误小概率逻辑错乱关键是否触达栈保护页报段错误堆数组动态分配越界时大概率读取垃圾值极小概率段错误是否超出进程地址空间报答案错误15.关于realloc函数使用的易错点realloc(arr,sizeof(类型)*size)注意第二个参数是扩容后的总大小以字节为单位不是以类型个数为单位只写个size是错的!对于动态加入链表函数head在函数内是局部副本需要返回类型不为空才能传给主调函数!16.在C语言中NULL宏通常被定义为((void *)0)。这是一个将整数0强制转换为void类型的指针表示一个不指向任何有效内存地址的空指针调试时可以用%s格式把空指针打印出null !17.全局变量包括静态局部变量的 int 类型会自动初始化为 0而普通局部变量栈上的 int 类型如果不手动初始化值是未定义的垃圾值具体来说在主函数外定义的int类型自动为0而在main函数内定义的int类型变量是垃圾值一般根据需要手动赋0才行18.当使用scanf结合!EOF判断时输入数据按回车并不算输入/文件 末尾的结束符scanf会跳过换行符继续阻塞I/O等待输入数据并读取所以这时可手动以输入-1或换行符表示结束或者边输入边输出运行结果19.exit是库函数用于退出程序编写自定义函数时注意避开系统已存在的函数名称20.隐藏的数组越界问题1) PTA天梯赛59题题目虽告知每行字符总长度不超过100我最开始定义的char str[105]但是题目要求替换部分内容而替换后的总长度超过了 105个字符导致越界遇到这种问题都先试试扩大数组能否解决问题比如扩大char str[200]另外可以复习一下此题的解题思路尤其是如何找到最后3个单词并进行拼接覆盖相当于替换的效果2)段错误时一般考虑数组越界而数组越界分为内存溢出和索引异常如果确定数组大小足够大那就考虑索引异常过大或为负索引这时候仔细审查索引的控制是否可能出现过大/为负数的情况21.如果排除了越界错误且部分答案正确仔细读题重新审题严格按照题目要求复现同时挖掘边界和极端情况关于极端输入的找法如果从题目中不明显顺着代码数据流向找判断每一步是否有逻辑漏洞漏掉一些条件/极端情况22.复现PTA天梯赛88题尤其是has_extra位置的定义控制的是不同分数而不是批次顺序学习思路对一批分数先排序然后从小到大选遇到同分的处理int last_score;遍历分数数组如果last_score就取并更新last_score如果last_score继续根据条件判断是否可选不用更新last_score但如果要求了同一批次、同一分数下只能多选一个额外名额则需要更新额外名额的标志位has_extra !此方法先升序或降序排序让相同的值挨着排列再更新last_number也可用于int型数组去重