Re: [問題] qsort compare 與 strcmp 轉型問題
※ 引述《manoeuvre (策)》之銘言:
: qsort 的參數4 沒辦法直接丟 strcmp 進去,
: 是因為型態的問題嗎?還是兩個 compare 比較對象不同?
: ----
: QSORT(3) FreeBSD Library Functions Manual QSORT(3)
: void
: qsort(void *base, size_t nmemb, size_t size,
: int (*compar)(const void *, const void *));
: STRCMP(3) FreeBSD Library Functions Manual STRCMP(3)
: int
: strcmp(const char *s1, const char *s2);
: ----
: 原本的想法是
: qsort 的參數 要傳的 compare function 參數型態是 (const void *, const void *)
: strcmp 的參數型態是 (const char *, const char *)
: 所以參數要從 void 換成 char
: 不過不知道怎麼轉型這種函式庫的 function ,所以 Google 出這個:
: http://twpug.net/docs/ccfaq/node228.html
: ----
: int pstrcmp(const void *p1, const void *p2)
: {
: return strcmp(*(char * const *)p1, *(char * const *)p2);
: }
: ----
: 不過看到這裡,感覺像不只是 void -> char 的問題而已
: 看很久看不懂 return strcmp(*(char * const *)p1, *(char * const *)p2);
: 的意義為何..... 網頁好像一堆大陸用語,也看不太懂.....
: 意思是不是,qsort 和 strcmp 的比較對象不同?
: 所以要另外生一個用指標指向字串的 compare function?
: 那行轉型的邏輯也搞不清楚...
: 另外,這麼看來每次要用 qsort 時 compare function 都要自己寫一個來轉型才行囉?
: 原本系統裡的函式庫是不是都無法直接塞入 qsort?
: 抱歉問題很多,謝謝解惑!
嚴格說來, void * 和 char * 是「兩種」指標
標準只保證說你像上面 pstrcmp 這樣明寫著轉型時能夠轉得過去
因此如果要寫 portable 的程式的話 使用類似上面這個 pstrcmp 這種才是正道
這個 pstrcmp 所使用的情境是
那個要排序的字串陣列裡面存放的是指向每個字串的指標
(也就是說是個指標的陣列 每個元素是個指標 指向字串)
也就是說這 qsort 的呼叫是長得這樣:
char *strarray[MAX];
...
qsort(strarray, count, sizeof(char *), compare);
這麼一來 由 qsort 傳進 pstrcmp 的 p1 p2 這兩個指標會是那個陣列上的位置
那將是個指向一個 char * 的指標
於是我們必須將其傳入的 const void * 轉型成 char * const * 再提取之才能得到字串
這便是那一行的意義了
有的時候 (我想這才是你的問題) 字串陣列是直接使用字元的二維陣列表示的
這時 qsort 的呼叫會長得像這樣:
char strarray[MAX][MAXLEN];
...
qsort(strarray, count, sizeof(strarray[0]), compare);
第三個參數表示這二維陣列的每一列多大 在上行的情形裡就會是 MAXLEN
這時的 compare 是這樣寫的:
int compare(const void *p1, const void *p2)
{
return strcmp((const char *)p1, (const char *)p2);
}
一樣的道理 由於 qsort 傳進 compare 的 p1 p2 會是指向 strarray 的一列的指標
因此直接將它轉型成 const char * 即是我們的字串了
照理來說啦 這裡理應也要寫個像這樣的 compare 函式
不過有些人(如原 PO)會想要把 strcmp 直接塞進 compare 的位置
(扯了一長串總算回到原題了...)
這樣的 hack 是有的
由於 strcmp 根本就只是拿兩個指標當參數
因此如果讓 strcmp 直接收 qsort 傳進去的那兩個 const void * 指標的話
只要「把 const void * 指標的樣子直接當成 const char * 來解釋」行得通的話
(用 C++ 的話來說就是: 若 compare 函式裡的 const void *p1 滿足 (p2 亦同)
reinterpret_cast<const char *>(p1) == static_cast<const char *>(p1)
(直接硬解為 const char *) (正常轉型成 const char *)
這個等號的話)
那 strcmp 就能如你所想的工作
這麼的話 要做的事情就是把 strcmp 假裝成
int (*)(const void *, const void *)
這個型態的函式指標 (它正是 qsort 第四個參數的型態)
也就是
(int (*)(const void *, const void *))strcmp
把上面這一串丟到 qsort 的第四個參數去
實際上 compiler 在編譯時認為這樣轉過去應該沒什麼問題
就會直接把 strcmp 的位址丟進去給 qsort
於是就這樣什麼事都沒有 工作完成了
不過這個 hack 要有上面這個條件成立才行 (雖然一些常見平台是都成立的沒錯啦...)
因此嚴格說來這只能算是 hack 不是個 portable 的寫法
要 portable 的話還是乖乖寫 compare 函式吧 (因為很重要所以要說兩次(爆
--
1985/01/12 三嶋鳴海 1989/02/22 優希堂悟 1990/02/22 冬川こころ 1993/07/05 小町
つぐみ 歡迎來到 1994/05/21 高江ミュウ 1997/03/24 守野いづみ 1997/03/24 伊野瀬
チサト 1998/06/18 守野くるみ 打越鋼太郎的 1999/10/19 楠田ゆに 2000/02/15 樋口遙
2002/12/17 八神ココ 2011/01/11 HAL18於朱倉岳墜機 ∞與∫的世界 2011/04/02 茜崎空
啟動 2012/05/21 第貮日蝕計畫預定 2017/05/01~07 LeMU崩壞 2019/04/01~07 某大學合宿
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.112.28.91
→
03/18 03:24, , 1F
03/18 03:24, 1F
→
03/18 03:26, , 2F
03/18 03:26, 2F
其實把 const 拿掉出現的東西你應該比較熟悉: char ** 就是個指向 char * 的指標
加 const 在那表示這個指標指向的東西不能變動
其實這樣對比就清楚了:
const void * ==等同於== void const * 指標指向不知啥 但不能變動它
char * const * 指標指向char * 但不能變動它
然後也因為 qsort 有間接這一層的關係
(qsort 傳入比較函式的是指向欲比較元素的 const void * 指標)
這就不能使用文中所述的那個 hack 了 而必須要另寫一個比較函式出來
※ 編輯: LPH66 來自: 140.112.28.91 (03/18 15:33)
推
03/20 02:59, , 3F
03/20 02:59, 3F
討論串 (同標題文章)
本文引述了以下文章的的內容:
完整討論串 (本文為第 2 之 2 篇):