[英]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 *a
和TRELLIS_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.