1

我不知道我哪里出错了。下面的代码预计将接受用户输入的奥运会游泳运动员的 fname、lname 国家和完成时间,并按以下最快时间对结果进行 qsort;

**样本输入**
阿德灵顿丽贝卡 GBR 4:03.01
MUFFAT 卡米尔 FRA 4:01.45  
FRIIS 乐天 DNK 4:03.98
施密特艾莉森美国 4:01.77

**样本输出**
MUFFAT 卡米尔 FRA 4:01.45
施密特艾莉森美国 4:01.77
阿德灵顿丽贝卡 GBR 4:03.01
FRIIS 乐天 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;
}
4

2 回答 2

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()格式说明符,中用于分隔最后两个ints。改成:

if (6 == scanf("%14s %14s %3s %d:%d.%d\n", ...
于 2012-08-29T14:18:27.277 回答
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);

我的编译器抱怨&,和. 如果您的编译器没有这样做,请调高警告级别,直到它这样做,或者获得更好的编译器。minssecsfsecs

您的运动员打印代码使用 的otime子结构struct olympians,但您的代码没有设置它(并且它复制了单独成员minssecs、中的值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, ...);的接口报告错误printf()err_error()也退出。这将 4 行错误报告减少到只有 1 行,这意味着我更有可能这样做。

对于输入数据(注意.和的开关,):

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而不是修复。%d

困难的部分是,如果输入时间字符串是:

4:03,1

它需要被视为 4:03,10 而不是 4:03,01。更糟糕的是,4:3,9 应该是 4:03,90,而不是 4:03,09;这让运动员在排序中获得了 0.81 秒的优势,仅仅是因为省略了尾随的零(所以它确实很重要)。这将需要不同的输入代码;我什至可以用 将小数部分读入长度为 3 的字符串%2[0-9],然后将其后处理成一个数字。这样您就可以判断输入的是一位数还是两位数。

time顺便说一句,您可以通过直接对组件进行排序来避免转换为完全,然后比较器的系统结构变得有益:

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;
    int rc;
    if (ia->mins > ib->mins)
        return +1;
    else if (ia->mins < ib->mins)
        return -1;
    else if (ia->secs > ib->secs)
        return +1;
    else if (ia->secs < ib->secs)
        return -1;
    else if (ia->fsecs > ib->fsecs)
        return +1;
    else if (ia->fsecs < ib->fsecs)
        return -1;
    else if ((rc = strcmp(ia->lname, ib->lname)) < 0)
        return -1;
    else if (rc > 0)
        return +1;
    else if ((rc = strcmp(ia->fname, ib->fname)) < 0)
        return -1;
    else if (rc > 0)
        return +1;
    return 0; 
}

(总的来说,您已经非常接近于正确编写代码了——做得很好。)

轻度被黑但有效的代码

并非所有推荐的更改都在此代码中,但它或多或少会产生合理的输出。

#include <stdio.h>
#include <stdlib.h>

struct mtime_t {
    int mins;
    int secs;
    int fsecs;
};
typedef struct mtime_t mtime;

struct olympians { 
    char fname[15];
    char lname[15];
    char country[4];
    int time;
    int mins, secs, fsecs;
    mtime otime;
};

/* qsort struct comparison 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); 
}

static void print_athlete(const struct olympians *ath)
{
    printf("%s %s %s \t %d:%d,%d\n", ath->lname, ath->fname, ath->country,
            ath->mins, ath->secs, ath->fsecs);
}

/* struct array printing function */ 
void print_struct_array(struct olympians *array, size_t len) 
{ 
    size_t i;
    for(i=0; i<len; i++) 
        print_athlete(&array[i]);
    puts("\n");
} 

/* sorting structs using qsort() */ 
void sort_structs_time(void) 
{ 
    int i, ath_num;

    struct olympians *ath_recs;
    scanf("%d", &ath_num);
    printf("Processing %d athletes\n", ath_num);

    ath_recs = (struct olympians *) malloc(ath_num*sizeof(struct olympians));
    if (ath_recs == 0)
    {
        fprintf(stderr, "Out of memory\n");
        exit(1);
    }

    for(i = 0; i < ath_num; i++) {
        if (scanf("%14s %14s %3s %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)
        {
            fprintf(stderr, "Ooops\n");
            exit(1);
        }
        ath_recs[i].time = (ath_recs[i].mins * 60 + ath_recs[i].secs) * 100 +
                               ath_recs[i].fsecs;
        print_athlete(&ath_recs[i]);
    }

    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(void) 
{ 
    /* run the function */
    sort_structs_time();
    return 0;
}
于 2012-08-29T14:46:21.750 回答