簡體   English   中英

這個C qsort函數調用中的參數是什么?

[英]What are the parameters in this C qsort function call?

qsort(bt->rw[t], bt->num[t], 
      sizeof(TRELLIS_ATOM *), 
      (int (*)(const void *,const void *))compare_wid);

bt->rw[t]是一個指向struct指針的指針, bt->[num]是一個int ,我不明白第四個參數是什么,除了compare_wid是在某處定義的函數,如下所示:

static int compare_wid( TRELLIS_ATOM* a, TRELLIS_ATOM* b )
{
   ...
   return x;
}

我將稍微了解該行的含義,但在我這樣做之前,讓我們了解為什么qsort()需要它所需類型的最終參數的一些基礎知識。 qsort()是一個可以對任何類型的數據進行排序的函數。

你提供:

  • 一個基指針,指向一個連續數據塊的開頭,
  • 塊中的元素數量,
  • 一個數據成員的大小,和
  • 一個比較兩個數據值的函數。

由於排序算法通常不依賴於正在排序的數據的類型,因此可以在不知道它正在排序的數據類型的情況下編寫qsort() 但是為了能夠做到這一點, qsort()接受void *參數,這意味着C中的“泛型指針”。

假設您有一組未排序的int值:

#define N 1024
int data[N] = { 10, 2, 3, -1, ... } /* 1024 values */

然后你可以通過調用qsort()來對它們進行排序:

qsort(data, N, sizeof data[0], compare_int);

data的類型為int *傳遞給時qsort()並且所述第一參數qsort()的類型為void * 由於任何對象指針都可以在C中轉換為void * ,這沒關系。 接下來的兩個論點也沒問題。 最后一個參數compare_int應該是一個帶有兩個const void *參數並返回一個int const void * 該函數將由qsort()調用,並且需要多次從&data[0]&data[N-1]的指針。

聲明一個函數f() “接受兩個const void *參數並返回int ”:

int f(const void *, const void *);

如果想要聲明一個我們可以設置為f的函數指針,則指針被聲明為:

int (*pf)(const void *, const void *);
pf = f;

括號是必需的,因為否則pf將是一個返回int *的函數。 現在, pf是一個指向返回int的函數的指針。

回到我們的int排序算法,從上面我們可以將compare_int()定義為:

int compare_int(const void *a, const void *b)
{
    const int *the_a = a;
    const int *the_b = b;
    if (*the_a > *the_b) return 1;
    else if (*the_a < *the_b) return -1;
    else return 0;
}

在編寫compare_int() ,我們知道傳遞的指針是int *偽裝成void * ,所以我們將它們轉換回int * ,這是正常的,然后我們比較數字。

現在,我們將注意力轉向相關代碼:

static int compare_wid( TRELLIS_ATOM* a, TRELLIS_ATOM* b )

表示compare_wid是一個帶有兩個TRELLIS_ATOM *參數的函數,並返回一個int 正如我們剛才看到的那樣, qsort()的最后一個參數應該是一個類型為的函數:

int (*)(const void *, const void *)

即,一個函數采用兩個const void *參數並返回int 由於類型不匹配,程序員將compare_wid()轉換為qsort()所需的類型。

但是,這有問題。 類型的功能:

int (*)(TRELLIS_ATOM *, TRELLIS_ATOM *)

不等同於類型的函數:

int (*)(const void *, const void *)

因此無法保證演員是否有效。 compare_wid()聲明為更簡單,正確和標准:

static int compare_wid(const void *a, const void *b);

compare_wid()的定義應如下所示:

static int compare_wid(const void *a, const void *b)
{
    const TRELLIS_ATOM *the_a = a;
    const TRELLIS_ATOM *the_b = b;
    ...
    /* Now do what you have to do to compare the_a and the_b */
    return x;
}

如果你這樣做,你就不需要在調用qsort()使用qsort() ,你的程序不僅更容易閱讀,而且更正。

如果你不能改變compare_wid() ,那么寫另一個函數:

static int compare_stub(const void *a, const void *b)
{
    return compare_wid(a, b);
}

並使用compare_stub() (不使用compare_stub() )而不是compare_wid()調用qsort() compare_wid()

編輯 :根據許多錯誤的答案,這是一個測試程序:

$ cat qs.c
#include <stdio.h>
#include <stdlib.h>

struct one_int {
    int num;
};

#ifdef WRONG
static int compare(const struct one_int *a, const struct one_int *b)
{
#else
static int compare(const void *a_, const void *b_)
{
    const struct one_int *a = a_;
    const struct one_int *b = b_;
#endif
    if (a->num > b->num) return 1;
    else if (a->num < b->num) return -1;
    else return 0;
}

int main(void)
{
    struct one_int data[] = {
        { 42 },
        { 1 },
        { 100 }
    };
    size_t n = sizeof data / sizeof data[0];

    qsort(data, n, sizeof data[0], compare);
    return 0;
}

使用compare()編譯,定義為采用兩個const struct one_int *值:

$ gcc -DWRONG -ansi -pedantic -W -Wall qs.c
qs.c: In function `main':
qs.c:32: warning: passing argument 4 of `qsort' from incompatible pointer type

使用正確的定義進行編譯:

$ gcc -ansi -pedantic -W -Wall qs.c
$

編輯2 :對於qsort()的最終參數使用compare_wid as-it-is的合法性似乎存在一些混淆。 comp.lang.c FAQ,問題13.9有一個很好的解釋(強調我的):

要理解為什么qsort比較函數中的好奇指針轉換是必要的( 以及為什么在調用qsort時函數指針的qsort無法幫助 ),考慮qsort如何工作是有用的。 qsort對被排序的數據的類型或表示一無所知:它只是在一小塊內存中亂竄。 (所有它知道的塊是它們的大小,你在qsort的第三個參數中指定。)為了確定兩個塊是否需要交換, qsort調用你的比較函數。 (要交換它們,它使用相當於memcpy 。)

由於qsort以通用方式處理未知類型的內存塊,因此它使用通用指針(void *)來引用它們。 qsort調用您的比較函數時,它將作為參數傳遞給要比較的塊的兩個通用指針。 由於它傳遞通用指針,因此比較函數必須接受通用指針,並在操作指針之前將指針轉換回適當的類型(即在執行比較之前)。 void指針與結構指針的類型不同,並且在某些機器上它可能具有不同的大小或表示(這就是為什么這些強制轉換是正確性的原因)。

如常見問題解答中所述,也可以看到這一點

(int (*)(const void *,const void *))表示“將后面的內容視為指向函數的指針,該函數接受類型為const void*兩個參數並返回一個int ”。 compare_wid確實是一個可以這種方式處理的函數。

qsort將調用此函數在排序時執行項目之間的比較:如果返回的整數為零,則假定項目相等,否則使用整數的符號對它們進行排序。

快速排序

void qsort ( void * base, size_t num, size_t size, 
                    int ( * comparator ) ( const void *, const void * ) );

比較器 :比較兩個元素的函數。 該功能應遵循以下原型:

int comparator(const void * elem1,const void * elem2);

該函數必須接受兩個指向元素的參數,類型為void *。 應將這些參數轉換回某些數據類型並進行比較。

該函數的返回值應表示elem1是否被認為小於,等於或大於elem2,分別返回負值,零或正值。

第四個參數包含對函數指針的顯式強制轉換:

int (*)(const void *,const void *)
  • int部分對應於返回類型
  • (*)部分是函數指針的表示法
  • (const void *,const void *)部分是參數類型
/* Integer comparison function for use with the stdlib's Qsort. */
inline int int_compare(const void *p1, const void *p2) {
        return ( *(int*)p1 - *(int*)p2 );
}

它是一個謂詞函數,由qsort實現調用以比較兩個TRELLIS_ATOM*

這是TRELLIS_ATOM類型元素的自定義排序函數(無論可能是什么)。

compare_wid應返回的東西<0,如果*a < *b ,> 0,如果*a > *b如果和0 *a*b在邏輯上視為相等。

這是一個quicksort函數,它需要一個指向自定義排序處理程序例程的函數指針。 因此,對第四個參數中的函數指針的參數進行類型轉換。

調用快速排序函數時,它會執行您提供的函數指針來處理排序算法,即TRELLIS_ATOM *aTRELLIS_ATOM *b ,然后在兩者之間進行比較檢查,並返回1,0,或邏輯比較中的-1,例如:

if (*a > *b) return 1;
if (*a == *b) return 0;
if (*a < *b) return -1;

暫無
暫無

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

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