刚开始做这些题,用c写的,有的因为语法上或者是一些小细节的问题,导致题目本身逻辑没有问题,但却会出现两种情况:
- 编辑器上的结果错误
- 编辑器上的结果正确,提交全部错误
下面来整理一下这个大二暑假断断续续做的pta上的题遇到的这些坑。
一、语法上
1、 scanf中没有给存储地址,就会出现异常退出的情况
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
int main()
{
int val;
scanf("%d", val); //&val
printf("%d", val);
return 0;
}
2、字符串的赋值要用strcpy()函数, 而不能直接将一个字符串赋值给一个字符指针,这样会让编译器认为是将一个字符串赋值给一个地址;比较两个字符串要用到strcmp()函数
3.给一个整型数组赋一个无穷大的值要用memset()的话,要赋的值就用0x3f。
3、向类似sprintf(), bsearch()这些函数,要搞清楚那些参数一个指针,那些不是,传参的时候不要搞错了,否则可能出现一些很迷惑自己的现象。
比如集装箱这道题
4、gets()函数
在做图书这道题时,要读入一个整数,但我用了gets(&val),最后真的惨不忍睹,函数不规范呀,把别的数据都给修改了,一个下午又没了。
二、细节上
1、cnt的值最终是-1, 而不是0
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
int main()
{
int cnt = 3;
while (cnt--)
{
}
printf("%d", cnt);
return 0;
}
2、double由于在程序中不能精确表示,如果对这一类型的数据多次累加可能造成最终结果错误
比如这一道题
3、整型和浮点型的输出问题,其实两个可以混用,我觉得这道题更精确地说是因为自己写的结果输出语句 printf("%.0lf\n", ans[i]); 中对于%.0lf的输出随编译器不同而不同,因为如果改成 printf("%.0lf\n", (ans[i] + 0.5)); 就会有一个测试用例正确。
互评成绩计算
4、读取数据流时,如果用到gets(a),或者scanf("%s", a)时,要注意用getchar()吸收换行,否则可能导致a字符数中是空的
比如检查密码这道题,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
int main()
{
char a[81];
int cnt, n, len, num_flag, alpha_flag, illegal_flag;
scanf("%d", &n);
getchar();
while (n--)
{
// getchar();gets()会将一行的所有输入都存下来,包括换行
// 所以如果是这里getchar()的话,除了第一个密码,其他的都会少第一个字母
// getchar();
gets(a);
// scanf("%s", a);
printf("pw:%s\n", a);
cnt = num_flag = alpha_flag = illegal_flag = 0;
len = strlen(a);
for (int i = 0; i < len; i++)
{
if (isdigit(a[i]))
{
num_flag = 1;
}
else if (isalpha(a[i]))
{
alpha_flag = 1;
}
else
{
if (a[i] != '.')
{
illegal_flag = 1;
}
}
}
if (len < 6)
{
printf("Your password is tai duan le.");
}
else if (illegal_flag)
{
printf("Your password is tai luan le.");
}
else if (num_flag == 0)
{
printf("Your password needs shu zi.");
}
else if (alpha_flag == 0)
{
printf("Your password needs zi mu.");
}
else
{
printf("Your password is wan mei.");
}
printf("\n");
}
return 0;
}
第一个getchar()不能缺少,而且这里不能用scanf()替代gets(),因为密码中可能有空格,而scanf()遇到换行,空格就会终止。
5、对于有些保留小数点后几位的,比如下面的val保留小数点后一位,输出。当值是0.01,应该输出0.0;而当值是-0.1时,没有判断的话会输出-0.0,实际上应该输出0.0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
int main()
{
double v = -0.03;
printf("%.1lf", v);
return 0;
}
6、变量要赋一个初值,还有的变量在判断中,或者循环中,要再合适的地方重新赋值,否则的话就会出错,比如银行排队,这道题,起初我的代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
struct c
{
int arr_tm;
int pro_tm;
}time[10004];
int cmp(struct c *a, struct c *b){
return a->arr_tm - b->arr_tm;
}
int k;
int findService(int *a){
int ind = 0;
for(int i = 1; i < k; i++){
if(a[ind] - a[i] > 0){
ind = i;
}
}
return ind;
}
int main()
{
int n, ind, cnt, hour, minu, sec;
int win[100] = {0};
long long cnt_time;
scanf("%d %d", &n, &k);
for(int i = 0; i < n; i++){
scanf("%d:%d:%d %d", &hour, &minu, &sec, &time[i].pro_tm);
time[i].arr_tm = hour * 3600 + minu * 60 + sec;
time[i].pro_tm *= 60;
// printf("hour:%d\n", hour);
}
qsort(time, n, sizeof(time[0]), cmp);
for(int i = 0; i < k; i++){
win[i] = 8 * 3600;
}
ind = 0;
cnt = 0;
//cnt_time = 0; //刚开始时cnt_time没有赋初值,找了半天
for(int i = 0; i < n; i++){
if(time[i].arr_tm > 17 * 3600){
break;
}
ind = findService(win);
cnt = i;
if(time[i].arr_tm < win[ind]){
cnt_time += win[ind] - time[i].arr_tm;
win[ind] += time[i].pro_tm;
}
else{
// win[ind] += time[i].pro_tm + (time[i].arr_tm - win[ind]);
win[ind] = time[i].pro_tm + time[i].arr_tm;
}
}
printf("%.1lf", cnt_time * 1.0 / (60 * (cnt + 1)));
return 0;
}
就因为上面的cnt_time变量没有赋初值0,在vscode中用测试数据测试的时候没有问题,而提交的时候全部错误。
三、经验总结
1、如果一个代码盯了很长时间没盯出来,可以过一两个小时之后再盯。
2、如果提交上去的结果出现一两个错误,那一般就是边界问题。注意考虑一些特殊情况,构造特殊测试数据进行测试。
3、如果有的时候输出结果比较奇怪,注意检查前面的计算是和否正确,然后将前面的结果输出看是不是自己想的结果。
4、对于向id这样唯一的东西,需要时可以把这个作为数组的index。有些时候有因为这样在进行排序的时候导致id和index对不上,这个时候可以考虑构造结构体排序。
5、ans是一个字符数组,strcpy(a, ans), printf(ans)等,遇到ans里的空字符0就直接结束了。
6、定义结构体是最好同时用typedef 定义一个别名,否则的话每次使用声明一个结构体变量时都需要在前面加上struct。