我不知道我哪里出错了。预计下面的代码接受奥运会游泳运动员的fname,lname国家和结束时间的用户输入,并在最快的时间内输出结果;
**Sample Input** ADLINGTON Rebecca GBR 4:03.01 MUFFAT Camille FRA 4:01.45 FRIIS Lotte DNK 4:03.98 SCHMITT Allison USA 4:01.77 **Sample Output** MUFFAT Camille FRA 4:01.45 SCHMITT Allison USA 4:01.77 ADLINGTON Rebecca GBR 4:03.01 FRIIS Lotte DNK 4:03.98
struct mtime_t {
int mins;
int secs;
int fsecs;
};
typedef struct mtime_t mtime;
struct olympians {
char fname[15];
char lname[15];
char country[3];
int time;
int mins, secs, fsecs;
mtime otime;
};
/* qsort struct comparision function (time float field) */
int struct_cmp_by_time(const void *a, const void *b)
{
struct olympians *ia = (struct olympians *)a;
struct olympians *ib = (struct olympians *)b;
return (int)(60*ia->time - 60*ib->time);
}
/* struct array printing function */
void print_struct_array(struct olympians *array, size_t len)
{
size_t i;
for(i=0; i<len; i++)
printf("%s %s %s \t %d:%d,%d\n", array[i].lname, array[i].fname,
array[i].country, &array[i].otime.mins,
&array[i].otime.secs, &array[i].otime.fsecs);
puts("\n");
}
/* sorting structs using qsort() */
void sort_structs_time()
{
int i, ath_num;
struct olympians *ath_recs;
scanf("%d", &ath_num);
ath_recs = (struct olympians *) malloc(ath_num*sizeof(struct olympians));
for(i = 0; i < ath_num; i++) {
scanf("%s %s %s %d:%d,%d\n", ath_recs[i].fname, ath_recs[i].lname,
ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs, &ath_recs[i].fsecs);
}
puts("\n");
/* print original struct array */
print_struct_array(ath_recs, ath_num);
/* sort array using qsort function */
qsort(ath_recs, (size_t) ath_num, sizeof(struct olympians), struct_cmp_by_time);
/* print sorted struct array */
print_struct_array(ath_recs, ath_num);
}
/* call to the function) */
int main()
{
/* run the function */
sort_structs_time();
return 0;
}
答案 0 :(得分:3)
一个问题是:
char country[3];
不足以容纳GBR
或任何3个字符的字符串,因为空终止符需要一个附加字符,它将被scanf()
追加。这将导致写入的内存不应该被写入,从而导致未定义的行为。改为:
char country[4];
建议限制scanf()
读取的字符数,以防止缓冲区溢出并检查scanf()
的返回值,以确保所有预期的分配都已完成:
if (6 == scanf("%14s %14s %3s %d:%d,%d\n", ...
请注意,输入中的时间格式为4:03.01
,但scanf()
格式说明符,
用于分隔最后两个int
。改为:
if (6 == scanf("%14s %14s %3s %d:%d.%d\n", ...
答案 1 :(得分:1)
另一个问题是您的输入和输出格式使用,
作为小数点;您的示例数据使用.
作为小数点。
另一个问题是您在输入期间未将time
字段设置为任何值。我可能会选择:
for (i = 0; i < ath_num; i++)
{
if (scanf("%s %s %s %d:%2d,%2d\n", ath_recs[i].fname, ath_recs[i].lname,
ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs,
&ath_recs[i].fsecs) != 6)
break;
ath_recs[i].time = (ath_recs[i].mins * 60 + ath_recs[i].secs) * 100 +
ath_recs[i].fsecs;
}
如果我是偏执狂,我会确保分钟,秒和小数秒都为零或正(非负)。我可能会编写代码来将整行读入fgets()
的缓冲区,然后用sscanf()
进行解析;它使错误检测更容易。
char buffer[4096];
int i = 0;
while (fgets(buffer, sizeof(buffer), stdin))
{
if (i >= ath_num)
...too much data...
if (sscanf(buffer, "%s %s %s %d:%2d,%2d\n", ath_recs[i].fname, ath_recs[i].lname,
ath_recs[i].country, &ath_recs[i].mins, &ath_recs[i].secs,
&ath_recs[i].fsecs) != 6)
{
...report problems...but I've got the whole line to identify which record
...is giving the problem — which helps you and the user...
break;
}
...other checking...
ath_recs[i].time = (ath_recs[i].mins * 60 + ath_recs[i].secs) * 100 +
ath_recs[i].fsecs;
i++;
}
...actual number of athletes is in i...
最好不要让用户进行数据计数;电脑擅长计算。您只需要随意分配数组,这需要小心谨慎以避免二次行为。
然后,在比较代码中,不需要乘以60:
/* qsort struct comparision function (time float field) */
int struct_cmp_by_time(const void *a, const void *b)
{
const struct olympians *ia = (struct olympians *)a;
const struct olympians *ib = (struct olympians *)b;
if (ia->time > ib->time)
return +1;
else if (ia->time < ib->time)
return -1;
...else if ... tie-breaker decisions - name? ...
return 0;
}
我使用比较器函数显示的结构,因为它是可扩展的,并且因为它避免了算术溢出的问题。在这个应用程序中,两次之间的差异不太可能导致问题,但避免麻烦仍然是个好主意,减去两个整数可能会导致溢出(环绕)。
在您的代码中:
printf("%s %s %s \t %d:%d,%d\n", array[i].lname, array[i].fname,
array[i].country, &array[i].otime.mins,
&array[i].otime.secs, &array[i].otime.fsecs);
我的编译器抱怨&
,mins
和secs
前面的fsecs
。如果您的编译器没有这样做,请调高警告级别,直到它完成,或者获得更好的编译器。
您的运动员打印代码使用otime
的{{1}}子结构,但您的代码未设置它(并且它复制了单独成员struct olympians
中的值,{{ 1}},mins
)。在调试代码时,这让我偏离了一点。这段代码有效。它包含一个单独的小函数secs
(应该是fsecs
)来打印单个运动员,而print_athlete()
代码使用它 - 但我也可以在锻炼时使用它为什么输入数据没有在输出中打印(该调用仍在代码中)。读取后回显输入是一种基本的调试技术。代码还会检查print_olympian()
是否成功。 (我通常有一个函数,如print_struct_array()
来打印一个复杂的结构到指定的文件。标签也被打印,这允许我注释每个调用转储函数。转储函数通常应该转储每个元素结构。)
在生产代码中,我有一组函数,例如malloc()
,它使用void dump_olympian(FILE *fp, const char *tag, const struct olympian *athlete);
函数等接口报告错误; extern void err_error(const char *fmt, ...);
也会退出。这将4行错误报告减少到1,这意味着我更有可能这样做。
对于输入数据(printf()
和err_error()
的音符切换):
.
输出结果为:
,
第一个块来自输入循环中的调试打印;当然,其他两个是前后图像,来自原始代码。
您需要解决一个问题(分两部分)。简单的一点就是输出:
4
ADLINGTON Rebecca GBR 4:03,01
MUFFAT Camille FRA 4:01,45
FRIIS Lotte DNK 4:03,98
SCHMITT Allison USA 4:01,77
应该是
Processing 4 athletes
Rebecca ADLINGTON GBR 4:3,1
Camille MUFFAT FRA 4:1,45
Lotte FRIIS DNK 4:3,98
Allison SCHMITT USA 4:1,77
Rebecca ADLINGTON GBR 4:3,1
Camille MUFFAT FRA 4:1,45
Lotte FRIIS DNK 4:3,98
Allison SCHMITT USA 4:1,77
Camille MUFFAT FRA 4:1,45
Allison SCHMITT USA 4:1,77
Rebecca ADLINGTON GBR 4:3,1
Lotte FRIIS DNK 4:3,98
甚至:
Rebecca ADLINGTON GBR 4:3,1
通过在打印格式中使用Rebecca ADLINGTON GBR 4:3,01
代替Rebecca ADLINGTON GBR 4:03,01
来解决此问题。
困难的部分是,如果输入时间字符串是:
%.2d
它需要被视为4:03,10而不是4:03,01。更糟糕的是,4:3,9应该是4:03,90,而不是4:03,09;这使得运动员在排序中获得0.81秒的优势,仅仅因为省略了尾随零(所以它确实很重要)。这将需要不同的输入代码;我甚至可以将分数部分读成长度为3的字符串%d
,然后将其后处理成一个数字。这样你可以判断是输入了一位还是两位数。
顺便提一下,您可以通过直接对组件部分进行排序来避免完全转换为4:03,1
,然后比较器的系统结构变得有益:
%2[0-9]
(总的来说,你非常接近让你的代码正确 - 做得好。)
并非所有建议的更改都在此代码中,但它会产生合理的输出,或多或少。
time