[英]Is there any restriction about the compare function used in qsort()
我編寫了以下代碼片段,以將字符串數組排序的順序最小化了它們的串聯:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int cmpstr(const void* p1, const void* p2){
int p1l = strlen((const char*)p1);
int p2l = strlen((const char*)p2);
int r = strncmp((const char*)p1, (const char*)p2, p1l<p2l?p1l:p2l);
if(r == 0 && p1l != p2l){
if(p1l < p2l){
return cmpstr(p1, (const char*)p2 + p1l);
}
return cmpstr((const char*)p1 + p2l, p2);
}
return r;
}
int main(){
const char* arrstr[] = {"93", "936", "15", "152", "946"};
int num = sizeof(arrstr) / sizeof(char*);
qsort(arrstr, num, sizeof(char*), cmpstr);
for(int i = 0; i < num; i++){
printf("%s\n", arrstr[i]);
}
}
這些字符串應按15 152 936 93 946
的順序排序。 我們希望93
在936
和946
之間,因為936 93
< 93 936
和93 946
< 946 93
(為清楚起見,忽略添加的空格)。
但是代碼沒有按預期工作。 盡管我對cmpstr()
測試完全符合我的預期,但該數組根本沒有排序。
我怎么了?
我注意到,當我將cmpstr()
的演員表部分從*(char* const*)
更改為(char*)
, qsort()
也不起作用。 這是為什么?
傳遞給qsort
的比較函數接收要比較的兩個數組元素的地址 。 由於每個數組元素都是char *
,因此每個元素的地址都是char **
。 因此,您缺少一種間接性。
您需要將每個參數char * const *
轉換為char * const *
,然后取消引用以獲取指向字符串的指針:
int cmpstr(const void* p1p, const void* p2p){
char *p1 = *(char * const *)p1p;
char *p2 = *(char * const *)p2p;
...
}
編輯:
因為您要遞歸調用此函數,所以您需要在遞歸函數周圍使用非遞歸包裝器函數,因為它們采用的參數不同:
// internal recursive function that takes two strings
static int cmpstr_int(const char* p1, const char* p2){
int p1l = strlen(p1);
int p2l = strlen(p2);
int r = strncmp(p1, p2, p1l<p2l?p1l:p2l);
if(r == 0 && p1l != p2l){
if(p1l < p2l){
return cmpstr_int(p1, p2 + p1l);
}
return cmpstr_int(p1 + p2l, p2);
}
return r;
}
// comparison function that extracts desired datatype from void * params
// and passes them to recursive function
static int cmpstr(const void* p1p, const void* p2p){
const char *p1 = *(char * const *)p1p;
const char *p2 = *(char * const *)p2p;
return cmpstr_int(p1, p2);
}
您的比較函數接收指向數組元素的指針。 每個元素都是一個指向char的指針,因此您將獲得一個指向char的指針。
比較邏輯也有些復雜。 這是一個工作版本:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int cmpstr(const void* a_, const void* b_)
{
const char *const *a = a_;
const char *const *b = b_;
int la = strlen(*a);
int lb = strlen(*b);
if (la == lb) {
/* same length - sort lexicographically */
return strcmp(*a, *b);
}
if (la < lb) {
/* a is shorter */
int result = strncmp(*a, *b, la);
if (!result) {
/* a is a prefix of b */
result = strcmp(*a, *b + la);
}
return result;
}
/* else, b is shorter - re-enter with arguments swapped,
and negate the result */
return -cmpstr(b_, a_);
}
int main() {
const char* arrstr[] = {"93", "936", "15", "152", "946"};
const size_t num = sizeof arrstr / sizeof *arrstr;
qsort(arrstr, num, sizeof *arrstr, cmpstr);
for (size_t i = 0; i < num; i++) {
printf("%s\n", arrstr[i]);
}
}
輸出:
15
152
936
93
946
如果您認為我上面的cmpstr()
與原始版本有太大cmpstr()
,請考慮一下此侵入性較小的代碼,該代碼使用您想要的遞歸比較,並使用單獨的包裝器使其適合qsort()
:
int compare_strings(const char *a, const char *b)
{
int la = strlen(a);
int lb = strlen(b);
int r = strncmp(a, b, la<lb?la:lb);
if (r == 0 && la != lb) {
if (la < lb) {
return compare_strings(a, b + la);
}
return compare_strings(a + lb, b);
}
return r;
}
int compare_strings_qsort(const void* a_, const void* b_)
{
const char *const *a = a_;
const char *const *b = b_;
return compare_strings(*a, *b);
}
我仍然不得不更改您的變量名,因為我發現p1l
之類的東西很難讀。 我可以進一步簡化一點,我認為這比原始功能和上面的第一次嘗試都清晰(但可能需要一些注釋):
int compare_strings(const char *a, const char *b)
{
const int la = strlen(a);
const int lb = strlen(b);
const int r = strncmp(a, b, la<lb?la:lb);
return (la == lb || r)
? r
: (la < lb)
? compare_strings(a, b + la)
: compare_strings(a + lb, b);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.