void qsort(void* base, size_t num, size_t size,
int (*compar)(const void*, const void*));
compar 回傳:< 0 (a0 (a>b)void*,函數內需轉型為實際型別指標
int cmp_int(const void* a, const void* b) {
int ia = *static_cast(a);
int ib = *static_cast(b);
return (ia > ib) - (ia < ib); // 避免減法溢位
}
qsort(arr, n, sizeof(int), cmp_int);
int cmp_char(const void* a, const void* b) {
return *static_cast(a) - *static_cast(b);
}
int cmp_double(const void* a, const void* b) {
double da = *static_cast(a);
double db = *static_cast(b);
return (da > db) - (da < db); // 或使用 fcmp 模式
}
struct Record {
double score;
int id;
};
int cmp_score(const void* a, const void* b) {
const Record* pa = static_cast(a);
const Record* pb = static_cast(b);
return (pa->score > pb->score) - (pa->score < pb->score);
}
qsort(records, n, sizeof(Record), cmp_score);
struct Point { int x, y; };
int cmp_xy(const void* a, const void* b) {
const Point* pa = static_cast(a);
const Point* pb = static_cast(b);
if (pa->x != pb->x) return pa->x - pb->x; // 主鍵:x 升序
return pb->y - pa->y; // 次鍵:y 降序
}
struct Student { int id; char name[32]; };
int cmp_name(const void* a, const void* b) {
const Student* pa = static_cast(a);
const Student* pb = static_cast(b);
return strcmp(pa->name, pb->name); // 字典序
}
Student* students[100];
// ... 填入指標 ...
int cmp_ptr(const void* a, const void* b) {
const Student* pa = *static_cast(a);
const Student* pb = *static_cast(b);
return strcmp(pa->name, pb->name);
}
qsort(students, n, sizeof(Student*), cmp_ptr);
struct Point { double x, y; };
Point pivot; // 基準點 (最低最左)
double cross(const Point& a, const Point& b, const Point& c) {
return (b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x);
}
double dist2(const Point& a, const Point& b) {
double dx = a.x - b.x, dy = a.y - b.y;
return dx*dx + dy*dy;
}
int cmp_polar(const void* a, const void* b) {
const Point* pa = static_cast(a);
const Point* pb = static_cast(b);
double cr = cross(pivot, *pa, *pb);
if (cr < 0) return 1; // pb 在 pa 左側 (順時針)
if (cr > 0) return -1; // pa 在 pb 左側
// 共線:遠點優先 (便於掃描移除內點)
return (dist2(pivot, *pb) < dist2(pivot, *pa)) ? 1 : -1;
}
template
int cmp_asc(const void* a, const void* b) {
const T& va = *static_cast(a);
const T& vb = *static_cast(b);
return (va > vb) - (va < vb);
}
// 使用
qsort(arr, n, sizeof(int), cmp_asc);
qsort(vec, n, sizeof(double), cmp_asc);
| 錯誤 | 後果 | 正確做法 |
|---|---|---|
return *(int*)a - *(int*)b; | 整數溢位導致錯誤排序 (INT_MIN - INT_MAX 為正) | 用三元運算或 (a>b)-(a |
| double 直接相減轉 int | 精度遺失、錯誤比較 | 用 (da>db)-(da |
忘記 const 正確性 | C++ 編譯警告/錯誤 | 參數加 const,轉型用 static_cast |
| 結構體含指標未深度比較 | 比較指標值而非內容 | 解參考指標比較實際資料 |
| 比較函數不具備嚴格弱序 | qsort 行為未定義 (可能死循環、崩潰) | 確保可傳遞性、反對稱性、非自反性 |
#include
#include
std::vector v = {...};
std::sort(v.begin(), v.end()); // 類型安全、內聯優化、支援 Lambda
std::sort(students, students+n, [](const Student& a, const Student& b){
return a.score > b.score; // 降序
});
std::sort 平均效能優於 qsort (避免函數呼叫開銷、型別安全、支援移動語意),現代 C++ 專案應優先使用。
掌握 qsort 比較函數寫法是 C/C++ 程式設計基礎功。關鍵在於:正確轉型 void*、避免整數溢位、處理浮點數比較、支援多鍵值排序、確保嚴格弱序。雖然現代 C++ 推薦 std::sort,但理解 qsort 仍有助於維護舊代碼、嵌入式開發與跨語言介接。