【问题标题】:Sorting structures with qsort and void pointer issues使用 qsort 和 void 指针问题对结构进行排序
【发布时间】:2019-05-25 21:07:35
【问题描述】:

我的任务是读取存储在字符串和双精度对中的数据,让用户输入应该排序的内容(格式为“n-v+”),然后使用 qsort 对其进行排序。我在编写比较函数时遇到问题。

我收到以下错误:

  • 警告:取消引用“void *”指针
  • 错误:不应忽略 void 值
  • 警告:控件到达非空函数的末尾 [-Wreturn-type]

这是带有函数的代码部分

typedef struct {
char name[32];
double value;
} record;

int sign1=0; //1 is +, -1 is -
int tip1=0; //1 is n, 2 is v
int sign2=0; //1 is +, -1 is -
int tip2=0; //1 is n, 2 is v

//comparison function
int compa(const void*p,const void*d){ //1 means first, -1 means second
    int result=0;
    record first;
    first=*p;
    record second;
    second=*d;
    if(tip1==1){ //compare for n first
        result=strcmp(first.name,second.name);  //first goes smaller
        if(result!=0) return sign1*result;
        else { //then for v
            if (first.value>second.value)result=-1; 
            else result=1;  
            return sign2*result;
            }
        }
    else if (tip1==2){//compare for v first
        if (first.value>second.value){
            result=-1; 
            return sign1*result;}
        else if (first.value<second.value) {
            result=1;
            return sign1*result;}
        else{ //then for n
            result=strcmp(first.name,second.name);  //first goes smaller
            if(result!=0) return sign2*result;
            else return sign2;
            }
        }   
}

【问题讨论】:

    标签: c function void-pointers qsort


    【解决方案1】:

    怎么做?

    我认为最好编写两对函数。在每一对中,一个将是微不足道的,否定调用该对中另一个的结果。例如,您有'名称升序'和'名称降序';您可能会使用原型int cmp_name_asc(const void *p1, const void *p2); 完整实现“名称升序”,然后将其中的另一个实现为:

    int cmp_name_des(const void *p1, const void *p2)
    {
        return -cmp_name_asc(p1, p2);
    }
    

    cmp_value_asc 和 cmp_value_des 对也是如此。

    int cmp_value_des(const void *p1, const void *p2)
    {
        return -cmp_value_asc(p1, p2);
    }
    

    如果您对开销的轻微差异感到非常不安,您可以使用静态内联函数来解决它,您可以调用两次,每次调用一次 cmp_value_des() 和 cmp_value_asc(),但这几乎肯定不值得。

    然后你就得写两个函数了:

    int cmp_value_asc(const void *p1, const void *p2)
    {
        const record *r1 = p1;
        const record *r2 = p2;
        if (r1->value < r2->value)
            return -1;
        else if (r1->value > r2->value)
            return +1;
        else
            return 0;
    }
    
    int cmp_name_asc(const void *p1, const void *p2)
    {
        const record *r1 = p1;
        const record *r2 = p2;
        return strcmp(r1->name, r2->name);
    }
    

    然后,当您解析参数时,您可以控制调用四个函数中的哪一个。这比使用这些全局变量更容易,并复制结构(尽可能避免)等等。

    为什么会出现错误?

    您收到“warning: dereferencing ‘void *’ pointer”消息是因为您有:

    record first;
    first=*p;
    

    你需要使用:

    record first = *(const record *)p;
    

    要获得正确的复制行为 — 将 void * 转换为正确的指针类型,然后取消引用它。除此复制信息;你不应该这样做(它完全不必要地减慢速度)。您应该使用:

     const record *firstp = (const record *)p;
    

    除了强制转换在 C 中并不重要(它在 C++ 中,如果你不小心在 C++ 代码中以这种方式排序——这也是一个坏主意)。还要注意,这使得初始化成为变量定义的一部分,而不是稍后作为单独的赋值。这通常是一种很好的技术——尤其是在 C++ 中。

    同样,您可能在同一行中遇到了“error: void value not ignored as it ought to be”错误。由于p 是void *,*p 是void,并且您试图使用void 值的结果——这是您不能做的。

    “warning: control reaches end of non-void function [-Wreturn-type]”警告是因为您的比较函数具有以下结构:

    if (tip1 == 1)
    {
        …code that returns a value…
    }
    else if (tip1 == 2)
    {
        …code that returns a value…
    }
    

    因为在 else if 块之后既没有 else 子句也没有 return 0; 或其他任何东西,所以原则上,该函数可以退出而不返回值,这就是警告告诉你的。

    我还会观察到你似乎没有使用 tip2(tip1 == 2 应该是 tip2 != 0 吗?)。

    代码是否应该按名称和值排序?

    经过进一步审查,您似乎打算进行两部分比较, 按名称第一个和第二个值排序,或者按值第一个和名称第二个排序,并且对于每个比较,升序或降序。对吗?

    如果是这样,您仍然可以创建前面显示的小函数,但将它们设为static,因为您不会在比较代码之外使用它们。然后您可以决定创建函数指针来完成这项工作:

    static int (*compare1)(const void *p1, const void *p2) = cmp_name_asc;
    static int (*compare2)(const void *p1, const void *p2) = cmp_value_des;
    

    您的解析代码将选择正确的函数名称以分配给compare1 和compare2。你的比较函数就变成了:

    int cmpa(const void *p1, const void *p2)
    {
        int rc = (*compare1)(p1, p2);        // Or int rc = compare1(p1, p2);
        if (rc == 0)
            rc = (*compare2)(p1, p2);
        return rc;
    }
    

    必须使用文件范围变量compare1 和compare2 有点麻烦,但比尝试在问题中的代码中内联执行所有操作要少得多。

    很容易将其扩展为按三个标准或 1..N 个标准排序(使用指向函数的指针数组,并将指针设置为 null 以指示不再进行比较)。最困难的部分是在 compare1 和 compare2 和等效项中设置正确的函数指针。

    工作代码

    这是显示上述大部分建议的代码(我并不担心降序比较器函数损失的最小性能):

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct
    {
        char name[32];
        double value;
    } record;
    
    static int cmp_value_asc(const void *p1, const void *p2)
    {
        const record *r1 = p1;
        const record *r2 = p2;
        if (r1->value < r2->value)
            return -1;
        else if (r1->value > r2->value)
            return +1;
        else
            return 0;
    }
    
    static int cmp_name_asc(const void *p1, const void *p2)
    {
        const record *r1 = p1;
        const record *r2 = p2;
        return strcmp(r1->name, r2->name);
    }
    
    static int cmp_name_des(const void *p1, const void *p2)
    {
        return -cmp_name_asc(p1, p2);
    }
    
    static int cmp_value_des(const void *p1, const void *p2)
    {
        return -cmp_value_asc(p1, p2);
    }
    
    static int (*compare1)(const void *p1, const void *p2) = cmp_name_asc;
    static int (*compare2)(const void *p1, const void *p2) = cmp_value_des;
    
    static int cmpa(const void *p1, const void *p2)
    {
        int rc = (*compare1)(p1, p2);
        if (rc == 0)
            rc = (*compare2)(p1, p2);
        return rc;
    }
    
    static void dump_records(const char *tag, int num, const record *data);
    
    int main(void)
    {
        record data[] =
        {
            /*
            random -n 15 -c -T '{ "%C%v%2:8w", %[20:100]f },' |
            awk '{ printf("%8s%s %-14s %5.2f %s\n", "", $1, $2, $3, $4, $5) }'
            Plus three lines duplicated on name and then change the value;
            plus three lines duplicated on value and then change the name.
            */
            { "Memrgi",      66.90 },
            { "Joeeeoahnm",  98.40 },
            { "Turner",      81.40 },
            { "Rebfno",      81.40 },
            { "Rebfno",      23.19 },
            { "Tecao",       66.30 },
            { "Guvoejnard",  62.40 },
            { "Zipnoib",     32.70 },
            { "Kerjruw",     49.60 },
            { "Vebin",       51.60 },
            { "Ghost",       51.60 },
            { "Reoe",        85.00 },
            { "Yepfenen",    84.60 },
            { "Yepfenen",    82.60 },
            { "Kopl",        94.80 },
            { "Soorwzeo",    15.40 },
            { "Soorwzeo",    85.40 },
            { "Nemigiat",    29.10 },
            { "Poisson",     79.40 },
            { "Sositpv",     79.40 },
            { "Giidahroet",  71.00 },
        };
        enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };
    
        dump_records("Before sorting", NUM_DATA, data);
    
        compare1 = cmp_name_des;
        compare2 = cmp_value_asc;
        qsort(data, NUM_DATA, sizeof(data[0]), cmpa);
        dump_records("After sorting by name descending, value ascending", NUM_DATA, data);
    
        compare1 = cmp_value_des;
        compare2 = cmp_name_asc;
        qsort(data, NUM_DATA, sizeof(data[0]), cmpa);
        dump_records("After sorting by value descending, name ascending", NUM_DATA, data);
    
        compare1 = cmp_value_asc;
        compare2 = cmp_name_asc;
        qsort(data, NUM_DATA, sizeof(data[0]), cmpa);
        dump_records("After sorting by value ascending, name ascending", NUM_DATA, data);
    
        compare1 = cmp_name_asc;
        compare2 = cmp_value_asc;
        qsort(data, NUM_DATA, sizeof(data[0]), cmpa);
        dump_records("After sorting by name ascending, value ascending", NUM_DATA, data);
    
        return 0;
    }
    
    static void dump_records(const char *tag, int num, const record *data)
    {
        printf("%s (%d):\n", tag, num);
        for (int i = 0; i < num; i++)
            printf("%2d: %-12s %5.2f\n", i + 1, data[i].name, data[i].value);
    }
    

    该程序的输出:

    Before sorting (21):
     1: Memrgi       66.90
     2: Joeeeoahnm   98.40
     3: Turner       81.40
     4: Rebfno       81.40
     5: Rebfno       23.19
     6: Tecao        66.30
     7: Guvoejnard   62.40
     8: Zipnoib      32.70
     9: Kerjruw      49.60
    10: Vebin        51.60
    11: Ghost        51.60
    12: Reoe         85.00
    13: Yepfenen     84.60
    14: Yepfenen     82.60
    15: Kopl         94.80
    16: Soorwzeo     15.40
    17: Soorwzeo     85.40
    18: Nemigiat     29.10
    19: Poisson      79.40
    20: Sositpv      79.40
    21: Giidahroet   71.00
    After sorting by name descending, value ascending (21):
     1: Zipnoib      32.70
     2: Yepfenen     82.60
     3: Yepfenen     84.60
     4: Vebin        51.60
     5: Turner       81.40
     6: Tecao        66.30
     7: Sositpv      79.40
     8: Soorwzeo     15.40
     9: Soorwzeo     85.40
    10: Reoe         85.00
    11: Rebfno       23.19
    12: Rebfno       81.40
    13: Poisson      79.40
    14: Nemigiat     29.10
    15: Memrgi       66.90
    16: Kopl         94.80
    17: Kerjruw      49.60
    18: Joeeeoahnm   98.40
    19: Guvoejnard   62.40
    20: Giidahroet   71.00
    21: Ghost        51.60
    After sorting by value descending, name ascending (21):
     1: Joeeeoahnm   98.40
     2: Kopl         94.80
     3: Soorwzeo     85.40
     4: Reoe         85.00
     5: Yepfenen     84.60
     6: Yepfenen     82.60
     7: Rebfno       81.40
     8: Turner       81.40
     9: Poisson      79.40
    10: Sositpv      79.40
    11: Giidahroet   71.00
    12: Memrgi       66.90
    13: Tecao        66.30
    14: Guvoejnard   62.40
    15: Ghost        51.60
    16: Vebin        51.60
    17: Kerjruw      49.60
    18: Zipnoib      32.70
    19: Nemigiat     29.10
    20: Rebfno       23.19
    21: Soorwzeo     15.40
    After sorting by value ascending, name ascending (21):
     1: Soorwzeo     15.40
     2: Rebfno       23.19
     3: Nemigiat     29.10
     4: Zipnoib      32.70
     5: Kerjruw      49.60
     6: Ghost        51.60
     7: Vebin        51.60
     8: Guvoejnard   62.40
     9: Tecao        66.30
    10: Memrgi       66.90
    11: Giidahroet   71.00
    12: Poisson      79.40
    13: Sositpv      79.40
    14: Rebfno       81.40
    15: Turner       81.40
    16: Yepfenen     82.60
    17: Yepfenen     84.60
    18: Reoe         85.00
    19: Soorwzeo     85.40
    20: Kopl         94.80
    21: Joeeeoahnm   98.40
    After sorting by name ascending, value ascending (21):
     1: Ghost        51.60
     2: Giidahroet   71.00
     3: Guvoejnard   62.40
     4: Joeeeoahnm   98.40
     5: Kerjruw      49.60
     6: Kopl         94.80
     7: Memrgi       66.90
     8: Nemigiat     29.10
     9: Poisson      79.40
    10: Rebfno       23.19
    11: Rebfno       81.40
    12: Reoe         85.00
    13: Soorwzeo     15.40
    14: Soorwzeo     85.40
    15: Sositpv      79.40
    16: Tecao        66.30
    17: Turner       81.40
    18: Vebin        51.60
    19: Yepfenen     82.60
    20: Yepfenen     84.60
    21: Zipnoib      32.70
    

    【讨论】:

      猜你喜欢
      • 2014-07-04
      • 2021-08-02
      • 1970-01-01
      • 2012-04-08
      • 2015-12-13
      • 2016-11-29
      • 2012-05-13
      • 2021-05-08
      相关资源
      最近更新 更多