簡單的說,如果你使用了某個function,那麼你就是call了一個function。如果系統或是函式是要求你給一個function pointer,這個function pointer指到一個實際的函式(多半這個函式是你自己寫的)。然後它會在適當的時間呼叫此function,則此function就是所謂的 callback function。因為這個function是被callback了。
範例:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define DEFAULT_BLOCK_SIZE (4096)
// 定義callback function的prototype。
typedef void (* CALLBACK) (int);
// 定義了一個名為ShowPercentage的函式。這就是我們的callback函式。
// 他的prototype必須與前面的CALLBACK宣告一致。
void ShowPercentage(int percentage)
{
fprintf(stderr, "%dn%nn", percentage);
}
// 定義了一個CopyFile的函式,這個函式會將參數source所指定檔案複製到
// target參數所指定的檔案去。而且每複製DEFAULT_BLOCK_SIZE數量的資料
// 就會呼叫一次callback參數所指到function一次。
void CopyFile(const char *source, const char *target, CALLBACK callback)
{
char buf[DEFAULT_BLOCK_SIZE] ;
struct stat fs ;
int fdSrc, fdTrg ;
int readBytes = 0, totalReadBytes = 0, percentage = 0;
fdSrc = open(source, O_RDONLY);
fstat(fdSrc, &fs);
fdTrg = open(target,O_CREAT|O_TRUNC|O_RDWR);
// 主要複製資料的迴圈
while((readBytes=read(fdSrc, buf, DEFAULT_BLOCK_SIZE)) > 0)
{
write(fdTrg, buf, readBytes);
totalReadBytes += readBytes ;
//複製資料後就呼叫callback函式去做顯示百分比的動作。
callback( (totalReadBytes*100)/fs.st_size);
}
close(fdTrg);
close(fdSrc);
}
int main(void)
{
// 這個範例中只是利用callback來顯示目前的進度。
// 實際上我們可以利用callback來做更多的動作。
CopyFile("A.TXT", "B.TXT", ShowPercentage);
return 0 ;
}
=====================================================
并不是只有winapi 使用的才叫callback 哦。
我们也可以在自己的模块里面给外部提供callback.
比如记log,发消息等等。
例如一个下载模块,需要发进度给用户程序。
下面是该模块的API
typedef void (CALLBACK* pfProgress)(int percent);
int Down(LPCSTR URL,LPCSTR localFile,pfProgress progress=NULL)
{
...
while(nSize<RealSize)
{
nRecv=recv()....
nSize+=nRecv;
if(progress)
{
progress(nSize*100/RealSize);
}
}
}
CALLBACK 宏的主要作用是防止因为函数参数个数传递的不正确,造成程序错误。
因为CALLBACK函数的栈是由调用方来清除,而标准C函数是在函数返回时清出。
所以对CALLBACK函数传多余/少于它的参数并不会造成栈的问题。
但是其效率比标准C函数低一点。(因为会做附加的清除栈,而不是由ret n返回)
========================================================
以下是後先生的《深入浅出MFC》中的一段,希望能有所帮助。 首先我要很快地解釋一下什麼是 callback 函式。凡是由你設計而卻由 Windows 系 統呼叫的函式,統稱為 callback 函式。這些函式都有一定的型態,以配合 Windows 的呼叫動作。 某些Windows API 函式會要求以callback 函式作為其參數之一,這些API 例如 SetTimer 、LineDDA 、EnumObjects 。通常這種API 會在進行某種行為之後或滿足某種 狀態之時呼叫該callback 函式。圖6-6 已解釋過LineDDA 呼叫 callback 函式的時機; 下面即將示範的EnumObjects 則是在發現某個Device Context 的GDI object 符合我們 的指定型態時,呼叫 callback 函式。 好,現在我們要討論的是,什麼函式有資格在 C++ 程式中做為callback 函式?這個 問題的背後是:C++ 程式中的 callback 函式有什麼特別的嗎?為什麼要特別提出討 論? 是的,特別之處在於,C++ 編譯器為類別成員函式多準備了一個隱藏參數(程式碼 中看不到),這使得函式型態與 Windows callback 函式的預設型態不符。 假設我們有一個 CMyclass 如下: class CMyclass { private : int nCount; int CALLBACK _export EnumObjectsProc(LPSTR lpLogObject, LPSTR lpData); public : void enumIt(CDC& dc); } void CMyclass::enumIt(CDC& dc) { // 註冊 callback 函式 dc.EnumObjects(OBJ_BRUSH, EnumObjectsProc, NULL); } C++ 編譯器針對 CMyclass::enumIt 實際做出來的碼相當於: void CMyclass::enumIt(CDC& dc) { // 註冊 callback 函式 CDC::EnumObjects(OBJ_BRUSH, EnumObjectsProc, NULL, (CDC *)&dc); } 你所看到的最後一個參數,(CDC *)&dc ,其實就是 this 指標。類別成員函式靠著 this 指標才得以抓到正確物件的資料。你要知道,記憶體中只會有一份類別成員函式, 但卻可能有許多份類別成員變數 --- 每個物件擁有一份。 C++ 以隱晦的 this 指標指出正確的物件。當你這麼做: nCount = 0; 其實是: this->nCount = 0; 基於相同的道理,上例中的 EnumObjectsProc 既然是一個成員函式,C++ 編譯器也 會為它多準備一個隱藏參數。 好,問題就出在這個隱藏參數。callback 函式是給 Windows 呼叫用的,Windows 並 不經由任何物件呼叫這個函式,也就無由傳遞 this 指標給callback 函式,於是導至 堆疊中有一個隨機變數會成為 this 指標,而其結果當然是程式的崩潰了。 要把某個函式用作 callback 函式,就必須告訴 C++ 編譯器,不要放 this 指標作為 該函式的最後一個參數。兩個方法可以做到這一點: 1. 不要使用類別的成員函式(也就是說,要使用全域函式)做為callback 函式。 2. 使用 static 成員函式。也就是在函式前面加上 static 修飾詞。 第一種作法相當於在C 語言中使用callback 函式。第二種作法比較接近OO 的精神。 我想更進一步提醒你的是,C++ 中的 static 成員函式特性是,即使物件還沒有產生, static 成員也已經存在(函式或變數都如此)。換句話說物件還沒有產生之前你已經 可以呼叫類別的 static 函式或使用類別的 static 變數了。請參閱第二章。 也就是說,凡宣告為 static 的東西(不管函式或變數)都並不和物件結合在一起, 它們是類別的一部份,不屬於物件。