文章目錄
- 一、回調函數是什么
- 二、qsort使用舉例
- 三、qsort函數的模擬實現
一、回調函數是什么
int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("請選擇:");scanf("%d", &input);switch (input){case 1:printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);break;case 2:printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("%d\n", ret);break;case 3:printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("%d\n", ret);break;case 4:printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = Div(x, y);printf("%d\n", ret);break;case 0:printf("退出游戲\n");break;default:printf("選擇錯誤,請重新選擇\n");}}while (input);return 0;
}
void Calc(int(*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}
最后改造后的結果:
int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}void menu()
{printf("********************************\n");printf("****** 1. add 2. sub *****\n");printf("****** 3. mul 4. div *****\n");printf("****** 0. exit *****\n");printf("********************************\n");
}void Calc(int(*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do{menu();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");}} while (input);return 0;
}
- (簡單來說:把一個函數的地址傳遞給了函數指針,在這個函數內部通過函數指針去調用他所指向的函數,這種通過函數指針調用函數的方式,被調用的函數就是回調函數。)
二、qsort使用舉例
- qsort是C語言中的一個庫函數,這個函數是用來對數據進行排序的,對任意類型的數據都能進行排序。(quiick sort 底層使用的快速排序的思想)
void qsort(void* base, //指向待排序數組的第一個元素的指針size_t num, //base指向數組中的元素個數size_t size,//base指向的數組中一個元素的大小,單位是字節int (*cmp)(const void*, const void*) //函數指針 - 傳遞函數的地址//);
而在 compar 函數中的實現結果如下:
int compareMyType (const void * a, const void * b)
{if ( *(MyType*)a < *(MyType*)b ) return -1;if ( *(MyType*)a == *(MyType*)b ) return 0;if ( *(MyType*)a > *(MyType*)b ) return 1;
}
2.1 使用qsort函數排序整型數據?
如果想使用 qsort 函數排序整型類型數據,就得提供一個比較 2 個整型的比較函數:
int cmp_int(const void* p1, const* p2)
{return(*(int*)p1 - *(int*) p2);
}int main()
{int arr[10] = { 3,1,5,6,9,8,7,2,4,10 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}
意思是,如果想要比較兩個“字符串”,兩個“結構體”等,就提供相應的比較函數,這樣qsort就實現了可以比較任何類型數據的功能了。
再舉個例子:
2.2 使用qsort排序結構數據
首先創建一個結構體
#include<stdio.h>
#include<string.h>struct Stu //學?
{char name[20];//名字int age;//年齡
};int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);}int cmp_stu_by_age(const void* p1, const void* p2)
{ return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}void Swap(char* buf1, char* buf2, size_t width)
{int i = 0;char tmp = 0;for (i = 0; i < width; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{int i = 0;for (i = 0; i < sz - 1; i++){int j = 0;for (j = 0; j < sz - 1 - i; j++){//if (arr[j] > arr[j + 1])if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void test3()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);}void test4()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);}int main()
{test3();test4();return 0;
}
struct stu
{char name[20];int age;
};
1.?假設按照姓名來比較
int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//因為 p1 的類型是 void*,所以我們要把它強制類型轉換成(struct Stu*),注意這個轉換是臨時的,所以要用括號括起來((struct Stu*)p1)->name。
}void test1()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);}
2.?假設按照年齡來比較
int cmp_stu_by_age(const void* p1, const void* p2)
{ return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}void test1()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);}
最后進入主函數調試,分別對 arr 進行監控
int main()
{test1();test2();return 0;
}
(注意:strcmp比較的不是字符串長度,而是對應位置上字符的大小!!)
三、qsort函數的模擬實現
在指針(3)的講解中,我們了解到冒泡排序的算法及引用,并定義了代碼 bubble_sort 函數。而了解了qsort這個庫函數后,我們能否可以將 buble_sort 函數改造成通用的算法,可以排序任意類型的數據?
答案肯定是可以的,可以通過 qsort 的模仿實現。
前面講到 qsort 是底層使用的快速排序,而我們自己定義的? bubble_sort 是通過冒泡排序的思路實現排序,不會有沖突。
代碼如下:
void Swap(char*buf1, char*buf2, size_t width)
{int i = 0;char tmp = 0;for (i = 0; i < width; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{//趟數int i = 0;for (i = 0; i < sz - 1; i++){//一趟內部的兩兩比較int j = 0;for (j = 0; j < sz-1-i; j++){//if (arr[j] > arr[j + 1])//比較兩個元素if(cmp((char*)base+j*width, (char*)base+(j+1)*width)>0){//交換兩個元素Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}
接下里我們逐步分析:
- 在? bubble_sort 的形參描寫的過程:
? ? ?1.不清楚要排序的數據的類型,所以用 void *base接收;
? ? ?2.base指向的數組的大小,單位是字節,用 size_t(無符號整型)?接收;
? ? ?3.base指向的數組中一個元素的大小,單位是字節,但是我們不清楚將來的數據類型是什么,所以我們可以用寬度 width 接收;
? ? ?4.void*類型指針可以接受任何類型的地址,可以寫成?(*cmp)(const void* p1, const void* p2)) ,返回類型是 int。
- bubble_sort 中的邏輯算法:
? ? ?1.首先我們清楚在冒泡排序中的趟數,和每一趟中所需要兩兩對比的對數是不會改變的,不論數據類型是怎么樣,都是采取這種對比過程。
? ? ?2.(1)需要改變就是 if ( ) 中的條件和交換過程 利用 cmp 的返回值是否大于零,如果大于零說明前者大于后者則進行交換(假設是升序排列)。如果我們要使用冒泡排序的思路,就要解決?arr[j] 和?arr[j + 1] 這兩個元素的地址傳達。
? ? ? ? (2)而我們只知道首元素 base 的地址以及一個元素的寬度 width,但具體的大小不清楚。假設是一組整型數組 int arr[10] = { 3,1,5,6,9,8,7,2,4,10 } ,width 就等于 4,所以我們可以把 base 強制類型轉換成 (char*)base,當指向首元素地址 3時為?(char*)base+0,指向下個元素地址 1時為(char*)base+width*(0+1),那么就可以寫成:
? ? ? ? ? ? ? ? ? ? ? ?if(cmp((char*)base+j*width, (char*)base+(j+1)*width)>0)
? ? ? ? (3)接下里就進入交換:
? ? ? ? ? ? ? ? ? Swap((char*)base + j * width, (char*)base + (j + 1) * width, width)
buf1 和 buf2相差的是一個 width(4個字節)那交換的時候是否就可以創建一個 int tmp = 0 進行交換呢?在void Swap函數中并不知道buf1.2是什么類型,只知道兩者之間相差一個元素是 4 個字節,所以只能一個字節一個字節得交換,四對字節分別交換,兩個整型就交換成功了。
- 最后我們把上述的結構體數據利用 bubble_sort 排序看是否成功:
#include<stdio.h>
#include<string.h>struct Stu //學?
{char name[20];//名字int age;//年齡
};int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);}int cmp_stu_by_age(const void* p1, const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}void Swap(char* buf1, char* buf2, size_t width)
{int i = 0;char tmp = 0;for (i = 0; i < width; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{int i = 0;for (i = 0; i < sz - 1; i++){int j = 0;for (j = 0; j < sz - 1 - i; j++){//if (arr[j] > arr[j + 1])if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}
void test3()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);}void test4()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);}int main()
{test3();test4();return 0;
}
最終監視一下 arr 中的地址內容:
1.按姓名排序
2.按年齡排序
未完待續~~