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

C/C++ 回呼函式入門:原理、優勢與實作範例

     編輯:VC 教學

回呼函式對初學者常顯神秘。本文解析何謂回呼、為何使用、適用場景,並提供完整動態庫排序回呼實作範例。

什麼是回呼函式?

簡單說:透過函式指標呼叫的函式。若將函式位址作為參數傳遞給另一函式,並在該函式內透過指標調用,即為回呼。

為何使用回呼?

  • 解耦調用者與被調用者:調用者不需知曉具體實作,只依賴函式原型約定。
  • 通用演算法框架:排序、搜尋、遍歷等演算法核心固定,比較/處理邏輯外部化。
  • 通知機制:計時器、枚舉視窗、非同步完成等事件通知。

經典 API 回呼範例

  • SetTimer(hwnd, id, interval, TimerProc):計時器到期呼叫 TimerProc 或發送 WM_TIMER。
  • EnumWindows(EnumWindowsProc, lParam):枚舉頂層視窗,對每個視窗呼叫回呼,回傳 FALSE 停止枚舉。

C++ 中的取捨

回呼源自 C 語言。現代 C++ 應優先使用:

  • 虛擬函式:物件導向多型,封裝狀態與行為。
  • 函式物件 / Lambda / std::function:攜帶狀態、型別安全、支援內聯優化。

僅在介接 C API、現有回呼介面、或極度效能敏感無虛函式開銷時使用函式指標回呼。

完整實作範例:通用排序 DLL

DLL 介面 (SortDLL.h)


typedef int (__stdcall *CompareFunction)(const BYTE*, const BYTE*);

#define DLLDIR __declspec(dllexport)
DLLDIR void __stdcall BubbleSort(BYTE* array, int size, int elemSize, CompareFunction cmp);
DLLDIR void __stdcall QuickSort(BYTE* array, int size, int elemSize, CompareFunction cmp);

DLL 實作 (SortDLL.cpp)


#include "SortDLL.h"

void __stdcall BubbleSort(BYTE* array, int size, int elemSize, CompareFunction cmp) {
    for (int i = 0; i < size-1; i++)
        for (int j = 0; j < size-i-1; j++)
            if (cmp(array + j*elemSize, array + (j+1)*elemSize) > 0)
                swap_bytes(array + j*elemSize, array + (j+1)*elemSize, elemSize);
}

void __stdcall QuickSort(BYTE* array, int size, int elemSize, CompareFunction cmp) {
    // 快速排序實作,同樣呼叫 cmp 比較元素
}

呼叫端約定

回呼函式 cmp(a, b) 必須回傳:

  • -1:a < b(a 排前)
  • 0:a == b
  • 1:a > b(a 排後)

客戶端使用範例


#include "SortDLL.h"

int __stdcall CompareInt(const BYTE* a, const BYTE* b) {
    int ia = *reinterpret_cast(a);
    int ib = *reinterpret_cast(b);
    return (ia < ib) ? -1 : (ia > ib) ? 1 : 0;
}

int main() {
    int arr[] = {5, 2, 9, 1, 3};
    BubbleSort(reinterpret_cast(arr), 5, sizeof(int), CompareInt);
    // arr 現為 {1,2,3,5,9}
    return 0;
}

關鍵點總結

  1. 回呼函式原型必須雙方嚴格遵守(呼叫慣例、參數型別、回傳值約定)。
  2. 使用 __stdcall 統一呼叫慣例,避免 DLL 邊界堆疊破壞。
  3. 回呼內不得拋出 C++ 異常跨越 DLL 邊界,需自行捕獲轉為錯誤碼。
  4. 現代 C++ 專案請改用 std::function + Lambda,獲得型別安全與捕獲上下文能力。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved