簡體   English   中英

具有qsort和void指針問題的排序結構

[英]Sorting structures with qsort and void pointer issues

我的任務是讀取存儲在字符串和雙對中的數據,讓用戶輸入應該對數據進行排序的形式(“ n-v +”形式),然后使用qsort對其進行排序。 我在編寫比較功能時遇到問題。

我收到以下錯誤:

  • 警告:取消引用“ 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;
            }
        }   
}

怎么做?

我認為最好編寫兩對函數。 在每對中,一個將是微不足道的,從而消除了在該對中調用另一個的結果。 例如,您有“名稱升序”和“名稱降序”; 您可能會使用原型int cmp_name_asc(const void *p1, const void *p2);來完全實現“名稱升序” 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_asccmp_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 ++,這也不是一個好主意)。 還要注意,這使初始化成為變量定義的一部分,而不是以后作為單獨的分配。 通常這是一種好技術,尤其是在C ++中。

同樣,您可能會收到' error: void value not ignored as it ought to be ,在同一行上error: void value not ignored as it ought to be 'error。 由於p是一個void **pvoid ,並且您試圖使用void值的結果—您不能這樣做。

warning: control reaches end of non-void function [-Wreturn-type]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子句也沒有return 0; else if塊之后也沒有其他任何內容,該函數原則上可以退出而不返回值,這是警告告訴您的內容。

我還將觀察到您似乎並沒有使用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;

您的解析代碼將為分配給compare1compare2選擇正確的函數名稱。 您的比較函數將變為:

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;
}

必須使用文件作用域變量compare1compare2 ,但與嘗試在問題代碼中全部內聯執行相比,麻煩得多。

很容易將其擴展為按三個條件或1..N個條件進行排序(使用指向函數的指針數組,並將指針設置為null表示不再進行比較)。 最難的部分是在compare1compare2及等效項中設置正確的函數指針。

工作代碼

以下代碼顯示了上面的大多數建議(我不為在比較器降序功能中損失的最低性能而煩惱):

#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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM