首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言進階:使用回調函數實現通用冒泡排序

     編輯:關於C語言

C語言進階:使用回調函數實現通用冒泡排序

在 C 語言中,要編寫一個同時能處理整數陣列、字串陣列甚至自訂結構體的排序函數,關鍵在於「將比較邏輯與排序演算法分離」。這正是回調函數(Callback Function)大放異彩的場合。本文將帶你一步步實作一個通用的冒泡排序,並深入剖析其中的指標運算與記憶體模型。

核心設計思路

通用排序函數需要四個關鍵資訊:

  • 基地址(void *base):資料陣列的起始位址
  • 元素個數(int count):待排序項目數量
  • 元素大小(int size):單一元素佔用的位元組數
  • 比較函數(int (*cmp)(const void*, const void*)):回調函數指標,決定排序順序

關鍵元件實作

1. 通用交換函數

void swap(void *p1, void *p2, int size) {
    for (int i = 0; i < size; i++) {
        char tmp = *((char *)p1 + i);
        *((char *)p1 + i) = *((char *)p2 + i);
        *((char *)p2 + i) = tmp;
    }
}

以 char* 逐位元組搬移,不受資料型別限制,完美支援任意大小的結構體。

2. 通用冒泡排序主體

void bubble(void *base, int count, int size, int(*cmp)(const void *, const void *)) {
    for (int i = 0; i < count - 1; i++) {
        for (int j = 0; j < count - i - 1; j++) {
            // 計算第 j 和 j+1 個元素的位址
            void *elem1 = (char *)base + size * j;
            void *elem2 = (char *)base + size * (j + 1);
            if (cmp(elem1, elem2) > 0) {
                swap(elem1, elem2, size);
            }
        }
    }
}

位址運算 (char *)base + size * j 是精髓:將 void* 強轉為 char* 後,每遞增 1 即前進 1 位元組,配合 size 即可精準定位任意型別元素。

3. 整數比較回調

int int_cmp(const void *p1, const void *p2) {
    int v1 = *(const int *)p1;
    int v2 = *(const int *)p2;
    return (v1 > v2) - (v1 < v2);  // 返回 1/0/-1
}

4. 字串比較回調

int str_cmp(const void *p1, const void *p2) {
    // 陣列元素是 char*,傳入的是 char** 型別
    const char *s1 = *(const char **)p1;
    const char *s2 = *(const char **)p2;
    return strcmp(s1, s2);
}

注意:字串陣列 char *arr[] 在記憶體中儲存的是指標,因此比較函數需雙重解參考。

完整可執行範例

#include <stdio.h>
#include <string.h>

// ... 上述 swap、bubble、int_cmp、str_cmp 實作 ...

int main() {
    // 測試整數排序
    int nums[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    int n = sizeof(nums) / sizeof(nums[0]);
    bubble(nums, n, sizeof(int), int_cmp);
    puts("整數排序結果:");
    for (int i = 0; i < n; i++) printf("%d ", nums[i]);
    putchar('\n');

    // 測試字串排序
    char *strs[] = { "dddd", "bbbb", "cccc", "aaaa" };
    int m = sizeof(strs) / sizeof(strs[0]);
    bubble(strs, m, sizeof(char *), str_cmp);
    puts("字串排序結果:");
    for (int i = 0; i < m; i++) printf("%s\n", strs[i]);

    return 0;
}

執行結果

整數排序結果:
0 1 2 3 4 5 6 7 8 9
字串排序結果:
aaaa
bbbb
cccc
dddd

進階延伸:qsort 原理解析

C 標準庫的 qsort 正是採用相同介面設計(基地址、個數、大小、比較函數),但內部採用快速排序,平均時間複雜度 O(n log n),遠優於冒泡的 O(n²)。理解本文機制後,閱讀 qsort 原始碼將毫無壓力。

常見坑點提醒

  1. 比較函數簽名必須完全匹配:int (*)(const void*, const void*),否則會產生未定義行為。
  2. 字串陣列傳遞的是指標:sizeof(char*) 而非 sizeof(char[])。
  3. 結構體排序時:比較函數中需轉型為 const StructType* 存取成員。
  4. const 正確性:比較函數參數加 const,保證不修改原始資料。

總結

回調函數模式實現了「演算法與資料分離」,是 C 語言實現多態的核心手法。掌握 void* 指標運算、函數指標語法與記憶體佈局,即可編寫出像 qsort、bsearch 這樣的通用工具函數,這正是從 C 初學者邁向系統級開發者的關鍵門檻。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved