文章目錄
- 前言
- 一、什么是回調函數
- 二、qsort函數的介紹(默認升序排序)
- 三、qsort函數的模擬實現(通過冒泡排序)
- 總結
前言
本文介紹了回調函數,qsort函數的使用,以用冒泡排序來模擬實現qsort函數
提示:以下是本篇文章正文內容,下面案例可供參考
一、什么是回調函數
- 前面的博客里面我介紹了函數指針變量的相關概念,而回調函數就是通過一個函數指針調用的函數。進一步說,如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,被調用的函數就是回調函數,注意哈,回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生的時候由另一方調用噠,用于對該事件或條件進行響應。
- 我們使用回調函數其實可以簡化代碼,省去一些冗余重復的操作
以下是實現一個計算器的代碼,我們在沒有使用回調函數之前:
#include<stdio.h>
int add(int a, int b)//加法{return a + b;}int sub(int a, int b)//減法{return a - b;}int mul(int a, int b)//乘法{return a*b;}
int div(int a, int b)//除法{return a / b;}
int main(){int x, y;int input = 1;int ret = 0;do{//菜單:
printf("*************************\n");printf(" 1:add 2:sub\n");printf(" 3:mul 4:div\n");printf("*************************\n");printf("請選擇:");scanf("%d", &input);switch (input){//這之后的代碼就較為冗余,重復之處比較多case 1:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;//這里之前的代碼較為冗余,重復地方過多case 0:printf("退出程序\n");break;default:printf("選擇錯誤\n");break;}} while (input);return 0;}
在實現計算器的過程中,我們可以發現在case語句中輸入輸出較為冗余,重復次數過多,這里我們就可以用回調函數來簡化代碼,設置一個操作函數,參數為函數指針變量來簡化代碼。具體操作如下:
#include<stdio.h>
int add(int a, int b)//加法{return a + b;}int sub(int a, int b)//減法{return a - b;}int mul(int a, int b)//乘法{return a*b;}
int div(int a, int b)//除法{return a / b;}
void calc(int(*pf)(int,int))
{
int ret = 0;
int x,y;
printf("輸入操作數:");
scanf("%d %d",&x,&y);
ret = pf(x,y);//這里我們通過函數指針調用相關函數,所被調用的函數即為回調函數。
printf("ret=%d\n",ret);
}
int main(){int x, y;int input = 1;int ret = 0;do{//菜單:
printf("*************************\n");printf(" 1:add 2:sub\n");printf(" 3:mul 4:div\n");printf("*************************\n");printf("請選擇:");scanf("%d", &input);switch (input){case 1:calc(add);break;case 2:calc(sub);break;case 3:calc(mul);break;case 4:calc(div);break;case 0:printf("退出程序\n");break;default:printf("選擇錯誤\n");break;}} while (input);return 0;}
很明顯我重新定義了一個函數calc,而其參數為函數指針變量,我們將我們要執行的加減乘除函數傳遞到函數中,就可以減少重復的輸入和輸出代碼,從而做到了簡化代碼的功效。
二、qsort函數的介紹(默認升序排序)
- qsort函數是我們C語言庫中用來專門用來排序的庫函數(頭文件為:stdlib.h)
- 定義聲明為:
- void qsort (void* base, size_t num, size_t size, int (compar)(const void,const void*));
- base代表待排序序列,num,為序列中元素個數,size,代表每個元素所代表的字節大小,最后一個參數為函數指針類型,是一個比較函數,用來闡述比較規則的。
- 對于最后一個參數函數指針類型的參數,我們通過它的返回值來確定具體那個元素在前,那個元素在后:
若返回值<0,則第一個指針指向的元素在前,第二個指針指向的元素在后;若返回值=0,則默認第一個指針指向的元素在前,第二個指向的元素在后;若返回值>0,則第一個指針指向的元素在后,第二個指針指向的元素在前。 - 使用qsort函數來排序整型數據:
代碼顯示:
#include<stdio.h>
#include<stdlib.h>
int int_cmp(const void *p1,const void*p2)
//實現泛式編程,我們定義void*指針,這樣就可以接受任何類型的數據。后面只需要強制類型轉換成我們所需要的數據類型即可。
{
return (*(int*)p1-*(int *)p2);
}
int main()
{
int arr[]={1,3,5,7,9,2,4,6,8,0};
int i =0;
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(int),int_cmp);//根據需要傳遞相應的參數。
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
printf("%d ",arr[i] );
}
printf("\n");
}
- 使用qsort函數排序結構數據
- 我們來進行結構體的排序以學生結構體為例,我們進行分別以學生的名字為依據和學生的年齡為依據進行比較。
以年齡為依據進行排序:
#include<stdio.h>
#include<stdlib.h>
struct Stu //定義學生結構體變量
{
char name[20];//學生名字
int age;//年齡
};
//我們先以學生的年齡為依據進行比較
int cmp_stu_by_age(const void * e1,const void * e2)
{
return ((struct Stu*)e1)->age-((struct Stu*)e2)->age;
}
int main()
{
struct Stu arr[]={{"zhangsan,20"},{"lisi,30"},{"wangwu,35"}};//這里我們定義結構體序列
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(s[0]),cmp_stu_by_age);
}
return 0;
這里的排序結果我們通過調試來顯示:
排序前:
排序后:
以名字為依據進行排序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>//我們需要調用字符串函數strcmp函數進行字符串的比較
struct Stu //定義學生結構體變量
{
char name[20];//學生名字
int age;//年齡
};
int cmp_stu_by_name(const void*e1,const void * e2){
return strcmp( ((struct Stu*)e1)->name,((struct Stu*)e2)->name);
}int main()
{
struct Stu arr[]={{"zhangsan,20"},{"lisi,30"},{"wangwu,35"}};//這里我們定義結構體序列
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(s[0]),cmp_stu_by_age);
}
return 0;
我們通過調試來顯示排序結果:
排序前:
排序后:
這里strcmp字符串比較函數的返回值正好符合我們qsort對于比較函數返回值的要求,二者可謂是不謀而合呀。
三、qsort函數的模擬實現(通過冒泡排序)
- 我在之前的博客里面已經實現過我們所熟悉的冒泡排序代碼算法:
#include<stdio.h>
void input(int* arr, int sz)//輸入待排序序列
{for (int i = 0; i < sz; i++){scanf("%d", arr + i);}
}void bubble_sort(int* arr, int sz)//冒泡排序算法
{for (int i = 0; i < sz-1; i++)//sz-1趟比較{int change = 1;//小優化節省時間for (int j = 0; j < sz-1 - i; j++){if (arr[j] > arr[j+1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;change = 0;}}if (change == 1)//說明已經有序 {break;}}
}
void print(int* arr, int sz)
{for (int i = 0; i < sz; i++){printf("%d ", *(arr + i));}printf("\n");
}
int main()
{int arr[10] = { 0};int sz = sizeof(arr) /sizeof(arr[0]);input(arr, sz);bubble_sort(arr, sz);print(arr, sz);return 0;
}
但是在這里我們為了響應qsort算法,我們應該根據qort函數中的參數來重新改編冒泡排序。
前面已經提到過qsort函數的函數聲明:
void qsort (void* base, size_t num, size_t size, int (compar)(const void,const void*));
這里我們用void指針來接受待排序序列,是一種泛式的編程,這里就可以接受任何數據類型的排序,這便是void指針的最大優勢。
我們用冒泡排序模擬實現qsort函數的代碼如下(這里我們以排序整型數據為例):
#include<stdio.h>
int int_cmp(const void*p1,const void * p2)//定義比較函數
{ return (*( int *)p1 - *(int *) p2);
}
void _swap(void*p1,void*p2,int size)
{
for(int i=0;i<size;i++)
{
//每一位字節都進行交換,從而做到整個數據類型進行交換。char tmp = *((char *)p1 + i);//我們轉換成char*類型可以理解為轉換成單位字節,然后乘上數據類型字節大小就可以表示任意數據類型*(( char *)p1 + i) = *((char *) p2 + i);*(( char *)p2 + i) = tmp;
}}
void bubble(void*base,int count ,int size,int(*cmp)(void*,void*))
//這里完全模仿qsort函數來定義的
{
for(int i=0;i<count-1;i++)
{
for(int j=0;j<count-1-i)
{
if(cmp((char*)base+j*size,(char*)base+(j+1)*size)>0)//我們轉換成char*類型可以理解為轉換成單位字節,然后乘上數據類型字節大小就可以表示任意數據類型
{_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
}
}
}
}
int main(){int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;bubble(arr, sizeof(arr) /sizeof(arr[0]), sizeof (int), int_cmp);for (i = 0;i<sizeof(arr)/sizeof(arr[0]); i++){printf( "%d ", arr[i]);}printf("\n");return 0;}
總結
本文主要介紹了一個嶄新的概念回調函數,并分析了qsort函數的使用,以及用冒泡排序來模擬qsort函數,如有錯誤,請批評指正,感謝支持