回調(diào)函數(shù)概念
回調(diào)函數(shù)就是一個通過函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一
個函數(shù),當這個指針被用來調(diào)用其所指向的函數(shù)時,我們就說這是回調(diào)函數(shù)?;卣{(diào)函數(shù)不是由該
函數(shù)的實現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時由另外的一方調(diào)用的,用于對該事件或
條件進行響應。
簡單的來說,回調(diào)函數(shù)就是通過函數(shù)指針調(diào)用的函數(shù)就是回調(diào)函數(shù)。我們庫里面有一個函數(shù)qsort,就用到了回調(diào)函數(shù)。
庫函數(shù)qsort(在頭文件stdlib.h中)
這個函數(shù)是一個排序函數(shù),我們學過冒泡排序,但是那個排序能夠排序整型,我們今天說的qsort可以排序任何類型,它默認排的是升序。我們來看一下他的參數(shù)列表:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
這里的void* 為無類型,因為可以排序任意類型的數(shù)組,所以不知道你的是哪種數(shù)組的指針用void* 來接收,size_t為無符號整型,num表示的是數(shù)組的大小,width為數(shù)組中每個元素多大,最后一個參數(shù)就是函數(shù)指針了,指向的是 int xxxx(const void *elem1, const void *elem2 ) 這樣一個函數(shù)。再看下圖:
如果elem1指向的內(nèi)容大于elem2指向的內(nèi)容就返回大于0的數(shù)字,如果等于就返回0,如果小于就返回小于0的數(shù)字。
那么接下來我們就上手來用下這個函數(shù):
// qsort需要的函數(shù)
int sort_int(const void* p1, const void* p2)
{
return *((int*)p1) - *((int*)p2);
}
// 打印函數(shù)
void print(int* arr, int size)
{
for (int i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 5,6,4,8,1,3,2,7,0 };
int sz = sizeof(arr) / sizeof(arr[0]);//計算數(shù)組大小
qsort(arr, sz, sizeof(arr[0]), sort_int);
print(arr, sz);
return 0;
}
運行結(jié)果:
需要主要的是,void* 不能直接對它解引用操作,需要先強制類型轉(zhuǎn)換,然后再操作。
qsort模擬實現(xiàn)
因為我們目前就學過冒泡排序,所以今天我們就用冒泡排序來模擬實現(xiàn)qsort.
我們將上面的代碼拿下來,把qsort改為my_qsort.
int sort_int(const void* p1, const void* p2)
{
return *((int*)p1) - *((int*)p2);
}
// 打印函數(shù)
void print(int* arr, int size)
{
for (int i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 5,6,4,8,1,3,2,7,0 };
int sz = sizeof(arr) / sizeof(arr[0]);//計算數(shù)組大小
my_qsort(arr, sz, sizeof(arr[0]), sort_int);
print(arr, sz);
return 0;
}
那現(xiàn)在我們只需要實現(xiàn)my_qsort就可以了,我們這里傳遞的是整型數(shù)組,所以可以用整型指針來接受,但是我們qsort是可以排序任何類型的數(shù)據(jù)的,所以我們要用void* 來接受。所以函數(shù)的參數(shù)列表為:
void my_qsort(void* arr, size_t num, size_t width, int (cmp)(const void, const void*))
我們冒泡排序不管排什么數(shù)據(jù),排的趟數(shù)肯定不會變的,而且每一趟比較的對數(shù)也是固定的,所以我們可以把冒泡排序的大體框架實現(xiàn)出來,這里的函數(shù)指針指向的函數(shù)就可以認為比較兩個元素的大小的函數(shù)。我們的比較函數(shù)需要兩個指針,也就是兩個元素的地址,這時候我們傳過來的元素大小就起到作用了,我們首元素的地址可以認為就是arr,第二個就是arr+1width,又因為只有arr是(char)類型是,我們才能實現(xiàn)每次跳的單位是1,所以我們在這之前要將arr強制轉(zhuǎn)化為(char*)類型因為我們拍的元素是變化的,我們這里將參數(shù)設置為
(char*)arr+j* width,(char*)arr+(j+1)*width,這樣我們就可以實現(xiàn)my_sort了。
void my_qsort(void* arr, size_t num, size_t width, int (*cmp)(const void*, const void*))
{
for (unsigned int i = 0; i < num - 1; i++)
{
for (unsigned int j = 0; j < num - 1 - i; j++)
{
if (cmp((char*)(arr)+j*width,(char*)(arr)+(j+1)*width)>0)
{
Swap((char*)(arr) + j * width, (char*)(arr) + (j + 1) * width,width);
}
}
}
}
這里就剩下一個Swap函數(shù)了,這個函數(shù)實現(xiàn)以后,我們的my_qsort就可以正常工作了。
因為我們還是要交換兩個元素,所以我們要將兩個元素的地址傳過去,傳過去的為(char*)類型的所以我們用char* 來接收就可以,又因為char*一次只能訪問一個字節(jié),如果我們要交換整型的話只需要將4個字節(jié)的內(nèi)容都交換就可以了,所以我們還需要講元素的大小過去。
void Swap(char* p1, char* p2, int width)
{
char tmp = 0;
for (int i = 0; i < width; i++)
{
tmp = *p1;
*p1 = *p2;
*p2 = tmp;
p1++;
p2++;
}
}
到這里我們的qsort就實現(xiàn)完成了,我們來看一下運行結(jié)果:
我們可以看到我們的排序也沒有問題。文章來源:http://www.zghlxwxcb.cn/news/detail-544575.html
今天的分享就到這里結(jié)束了,感謝大家的關(guān)注和支持!文章來源地址http://www.zghlxwxcb.cn/news/detail-544575.html
到了這里,關(guān)于【C】回調(diào)函數(shù)和qsort詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!