輸入 n 個字符串,按字典序(ASCII 碼順序)從小到大輸出。
#include
#include
#include
int cmp_str(const void *a, const void *b) {
// qsort 傳入的是元素指針,此處元素為 char*,所以轉為 const char**
const char * const *pa = (const char * const *)a;
const char * const *pb = (const char * const *)b;
return strcmp(*pa, *pb); // 利用 strcmp 比較字符串內容
}
int main() {
int n;
printf("請輸入字符串個數: ");
if (scanf("%d", &n) != 1 || n <= 0) return 1;
// 動態分配指針數組
char **strs = malloc(n * sizeof(char *));
if (!strs) return 1;
// 讀取每行字符串(含空格)
char buf[1024];
getchar(); // 吞掉 scanf 留下的 \n
for (int i = 0; i < n; i++) {
if (!fgets(buf, sizeof(buf), stdin)) { n = i; break; }
buf[strcspn(buf, "\n")] = '\0'; // 去掉換行符
strs[i] = strdup(buf); // 複製到堆內存
if (!strs[i]) { perror("strdup"); return 1; }
}
qsort(strs, n, sizeof(char *), cmp_str);
printf("\n排序結果:\n");
for (int i = 0; i < n; i++) {
puts(strs[i]);
free(strs[i]); // 釋放字符串內存
}
free(strs); // 釋放指針數組
return 0;
}
| 點 | 說明 |
|---|---|
qsort 原型 | void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)); |
| 比較函數 | 接收 const void* 指向元素,**元素類型決定轉換方式**。此處元素是 char*,故轉 const char* const* 再解引用。 |
strcmp | 返回 <0/=0/>0 對應字典序 < / = / >,完美匹配 qsort 要求。 |
| 動態分配 | malloc 指針數組 + strdup(或 malloc+strcpy)每條字符串,長度不限、無浪費。 |
| 內存釋放 | **雙重 free**:先 free(strs[i]) 字符串,再 free(strs) 指針數組,順序不可反。 |
| 輸入處理 | fgets 讀整行支援空格,strcspn(buf,"\n") 安全去換行,避免 gets 漏洞。 |
// 忽略大小寫排序
int cmp_icase(const void *a, const void *b) {
return strcasecmp(*(const char**)a, *(const char**)b); // POSIX,Windows 用 _stricmp
}
// 按長度升序,長度相同按字典序
int cmp_len_lex(const void *a, const void *b) {
const char *sa = *(const char**)a;
const char *sb = *(const char**)b;
size_t la = strlen(sa), lb = strlen(sb);
if (la != lb) return (la > lb) - (la < lb); // 避免溢出的三元技巧
return strcmp(sa, sb);
}
cmp_str(const char *a, const char *b) 簽名錯誤 → qsort 傳的是元素地址,元素是指針,所以是二級指針。char strs[100][100] → qsort(strs, n, 100, cmp) 但 cmp 接收 char(*)[100],轉換麻煩且浪費空間,**指針數組更靈活**。free → 內存洩漏。嚴格遵守「誰分配誰釋放」。scanf("%s") 讀字符串 → 遇空格截斷,改用 fgets。字符串排序 = 動態指針數組 + qsort + strcmp。
掌握 qsort 比較函數的**指針層級轉換**是關鍵:
元素類型 T → 比較函數參數 const void* → 轉 const T* → 解引用得 T → 比較。
此模式通用於任意類型數組排序(int、struct、double…)。