1. 回調函數
回調函數就是指通過函數指針調用的函數。
如果將函數指針作為參數傳遞給另一個函數,另一個函數根據指針來調這個函數,那么被調用的函數就是回調函數。回調函數不是由這個函數的實現方直接調用,而是在特定的條件下由另一方調用的。
例如在指針(3)中,我們將多個函數的地址放在函數指針數組中,根據不同的選擇來對輸入的x和y執行相應的計算,這里就是采用的就是回調函數的功能。
2.?qsort使用舉例
2.1 qsort函數
在 C 語言中,qsort是標準庫<stdlib.h>提供的快速排序函數,用于對數組進行排序。其函數原型如下:
void qsort(void *base, size_t nmemb, size_t size,int (*compar)(const void *, const void *));
其中:
base是要排序的數組首元素的地址
nmemb是數組中元素的個數
size是每個元素的大小,單位為字節
compar是指比較函數的指針,用來定義排序規則
2.2 利用qsort函數排序整型數據
qsort會根據傳遞的函數指針所指向的那個函數的返回值來排序,例如兩個值a,b進行比較:
當返回值為負時,a就會排在b前面;返回值為正時,a排在b后面;返回值為0時,qsort會將這兩個值視為相等,但并不會保證相等元素排序與原始排序相同,例如:原始排序是 a? b,a在前,而qsort排序后可能會是b? a,即相等的元素排序后的相對位置不一定與原始位置相同。
知道這些我們可以利用qsort函數做一個簡單的排序,讓一個數組中的元素按升序排序:
#include <stdio.h>
#include <stdlib.h>
int i;int cmp(const void* p1,const void* p2)
{return (*(int *)p1 - *(int *)p2);
}
int main()
{int arr[] = {1,4,3,6,8,9,7,2,5,0};int num = sizeof(arr)/ sizeof(arr[0]);int sz = sizeof(arr[0]);qsort(arr,num,sz,cmp);for(i = 0;i < num;i++){printf("%d ",arr[i]);}return 0;
}
0 1 2 3 4 5 6 7 8 9
我們可以看到輸出結果與預期一致,qsort正確得進行了排序。
另外,我們可以看到我們定義的cmp函數中,在進行運算前前面先將指針p1與p2的類型先強制轉換為了int *,這是因為void *雖然可以接受各種類型的指針,但是卻不能直接使用,必須先進行強制類型轉換。這里因為我們希望是升序,所以返回p1減去p2的值。若p1 < p2,返回值為負,p1排在p2前;若p1 > p2,返回值為正,p1排在p2后。如果我們想要實現降序,只需要將cmp函數體內p1與p2的位置互換或者是在計算表達式前加上 - ,改變正負即可。
2.3?使用qsort排序結構數據
現在我們嘗試利用qsort函數來對結構數據進行排序,例如:
#include <stdio.h>
#include <stdlib.h>
int i;
struct Stu
{char name[10];int age;
};
int cmp_by_name(const void *p1,const void *p2)
{return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);
}int main()
{struct Stu s[] = {{"zhangsan",20},{"lisi",23},{"wangwu",19}};int len = sizeof(s)/ sizeof(s[0]);int sz = sizeof(s[0]);qsort(s,len,sz,cmp_by_name);for(i = 0;i < len;i++){printf("%s\n",s[i].name);}return 0;
}
lisi
wangwu
zhangsan
根據打印結果,我們發現,qsort成功按照字母順序進行了排序。同樣的,我們也可以寫出cmp_by_age函數,讓qsort按照年齡來進行排序。
另外,我在定義函數cmp_by_name時,在函數體內部用 -> 來訪問結構中的name,而在打印時使用的是 . 。這是因為,我在定義函數的返回值時,這里的數據是指針類型,只能用->來訪問,而在打印時則是直接訪問結構,所以要使用 . 來訪問結構中數據。因此,如果我們不想使用->,也可以先對指針進行解引用操作然后再使用 . 來訪問數據。
3. qsort函數的模擬實現
學習了qsort函數的使用方法后,我們現在嘗試自己寫代碼來模擬實現qsort函數。
例如,我們用冒泡排序的方法法來模擬qsort函數。
那么。根據上面的代碼的經驗,我們可以得到下面的代碼:
#include <stdio.h>int cmp(const void *p1,const void *p2)
{return (*(int*)p1 - *(int*)p2);
}
void swap(void *p1,void *p2,int sz)
{int i;char temp;for(i = 0;i < sz;i++){temp = *((char *)p1 + i);*((char *)p1 + i) = *((char *)p2 + i);*((char *)p2 + i) = temp;}
}void bubble(void* base,int num,int sz,int (*cmp)(const void *p1,const void *p2))
{int i,j;for(i = 0;i < num - 1;i++){for(j = 0;j < num - 1 - i;j++){if(cmp(base + j * sz,base + (j + 1) * sz) < 0){swap(base + j * sz,base + (j + 1) * sz,sz);}}}
}
int main()
{int i;int arr[] = {1,5,3,7,9,4,0,2,6,8};int num = sizeof(arr)/ sizeof(arr[0]);int sz = sizeof(arr[0]);bubble(arr,num,sz,cmp);for(i = 0;i < num;i++){printf("%d ",arr[i]);}return 0;
}
9 8 7 6 5 4 3 2 1 0
這里主體的邏輯和上面的代碼一樣,值得一提的是swap這個函數。
在定義這個函數的時候我們是將指針類型強制轉換為char * 類型,這樣處理的好處是:我們為了模擬qsort函數實現通用設計,在不知道接受的數據類型的前提下,將指針類型強制轉換為char *類型,而char *類型指針和 i 相加時只會跳過 i 個字節,這樣能夠充分交換每一個字節中的數據,防止遺漏,保證了數據的準確性。?? ? ? ??