前言:上篇文章的末尾我們使用了轉移表來解決代碼冗余的問題,那我們還有沒有什么辦法解決代碼冗余呢?有的這就是接下來要說的回調函數。
往期文章:
指針1
指針2
指針3
指針4
文章目錄
- 一,回調函數
- 二,qsort實現快速排序
- 1,void*指針
- 三,qsort的模擬實現
一,回調函數
先來回顧一下上篇文章末尾的內容,寫一個模擬計算的代碼:
#include <stdio.h>
int add(int a, int b)
{ return a + b;
}
int sub(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("******* 0:exit ******\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 0: printf("退出程序\n"); break; default: printf("選擇錯誤\n"); break; } } while (input); return 0;
}
我們可以看到代碼是非常冗余的,想要解決冗余的問題方法一在我們上一篇文章指針4(轉移表)。方法二我們使用回調函數來實現。
那什么是回調函數呢?
回調函數說的是,如果你把函數的指針(地址)作為參數傳遞給另外一個函數時,當這個指針被用來調用其所指向的函數時,被掉用的函數就稱為回調函數。
我們給出具體的代碼:
#include<stdio.h>
int add(int x, int y)
{return x + y;
}
int sub(int x, int y)
{return x - y;
}
void clac(int (*pf)(int, int))
{int x = 0, y = 0;printf("請輸入兩個數:");scanf("%d %d", &x, &y);int ret = (*pf)(x, y);printf("計算結果為:%d\n", ret);
}
int main()
{int x, y;int input = 1;int ret = 0;do {printf("*************************\n");printf("*******1:add 2:sub ******\n");printf("******* 0:exit ******\n");printf("*************************\n");printf("請選擇:");scanf("%d", &input);switch (input){case 1:clac(add);break;case 2:clac(sub);break;case 0:printf("退出程序\n");}} while (input);
}
通過上面的兩段代碼對比我們有兩個改進之處:
一,將case語句中那些重復性的語句封裝到了一個新建的函數中。
二,函數調用發生了變化,從原來的直接調用add函數sub函數變成了先調用clac函數再去調用add函數sub函數。這種函數稱之為回調函數。
看明白這兩點相信已經不難理解回調函數了,但是還有一點需要注意:回調函數不是由該函數的實現方直接調用,而是在特定的事件或條 件發生時由另外的一方調用的,用于對該事件或條件進行響應。
下面畫一張圖讓你更好的理解:
二,qsort實現快速排序
在前面的文章中我們介紹了冒泡排序,這次我們來介紹一些qsort。q即quick快速的,sort是排序的意思。所以qsort就是快速排序俗稱快排。
那怎么使用qsort來實現快速排序呢?首先我們先得了解一些qsort:
![
void qsort(
void *base,
size_t num,
size_t width,
int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
通過專業文獻對qsort的描述我們知道qsort
共有4個參數第一個參數
void *base
是代表要傳入的目標數組名即所需要排序的數組名。size_t num
是代表要傳入的數組內有多少個元素。size_t width
是代表要傳入的數組內元素的大小。int (*compare )(const void *elem1, const void *elem2 )
是一個函數指針,函數指針的兩個參數 類型都為void*
也就是說在第四個參數中我們要傳入一個函數且這個函數具有比較elem1
和elem2
這兩個元素大小的功能。
我們寫一個排序整型數組的代碼來給大家舉例,讓大家能更好的了解qsort的使用方法:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
/*
void qsort(void* base, //第一個參數為需要比較的數組的首地址size_t num, //第二個參數為該數組的元素個數size_t width, //第三個參數為該數組每個元素的大小int(__cdecl* compare)(const void* elem1, const void* elem2));//第四個參數為函數指針,及傳入一個能比較數組內部元素大小的函數的地址
*///實現比較兩個函數大小的函數
int comp_int(const void* elem1, const void* elem2)
{return *(int*)elem1 - *(int*)elem2;//void*的指針不能直接使用 要強轉后再解引用才能使用 //elem1>elem2 返回大于零的數//elem==elem2 返回0//elem1<elem2 返回小于零的數
}
//打印整型數組
void print(int* P_arr, int sz)//一維數組傳參傳過去的是數組的首地址
{int i = 0;for (i = 0;i < sz;i++){printf("%d ", *(P_arr + i));}
}
//排整型的數組
int test1()
{int arr1[10] = { 1,4,3,2,6,5,8,7,9,10 };int sz = sizeof(arr1) / sizeof(arr1[0]);qsort(arr1, sz, sizeof(arr1[0]), comp_int);//print(arr1, sz);
}
int main()
{//寫一個test1來測試qsort排序整型數組test1();return 0;
}
我們來分析代碼的含義:
1,void*指針
上面諸多地方提到了void*指針,下面我們給出解釋:
在指針類型中有?種特殊的類型是
void*
類型的,可以理解為無具體類型的指針(或者叫泛型指 針),這種類型的指針可以用來接受任意類型地址。但是也有局限性,void*
類型的指針不能直接進行指針的±整數和解引用的運算。
#include <stdio.h>
int main() { int a = 10; int* pa = &a; char* pc = &a; return 0;
}
在上?的代碼中,將?個int類型的變量的地址賦值給?個char
*類型的指針變量。編譯器會警告,是因為類型不兼容。而使用void*
類型就不會有這樣的問題。
我們再看看void*
的指針接收別的類型的指針:
#include <stdio.h>
int main()
{ int a = 10; void* pa = &a; void* pc = &a; *pa = 10; *pc = 0; return 0;
}
這里我們可以看到,
void*
類型的指針可以接收不同類型的地址,但是無法直接進行指針運算。 那么void*
類型的指針到底有什么用呢? ?般void*
類型的指針是使?在函數參數的部分,用來接收不同類型數據的地址,這樣的設計可以實現泛型編程的效果。
理解了void*
類型,以及qsort如何使用了以后,我們就可以試著去排序一些其他類型的數據了。代碼內容在下載文件處取噢。
熟練了使用qosrt
來排序各種類型的數據后接下來我們就來模仿著造一個qosrt
函數。
三,qsort的模擬實現
我們之前學過了冒泡排序并且知道冒泡排序有一個缺點就是只能排序固定類型的數據,而qsortt
能排序任意類型的數據那我們能不能使用冒泡排序的方式來模擬實現qsort
快速排序呢?答案是肯定的。
我們先寫一個冒泡排序,然后再看看哪些地方需要修改:
#include<stdio.h>
void bubble_sort(int arr[],int sz)
{int i=0;for(i=0;i<sz-1;i++){int j=0;for(j=0;j<sz-i-1;j++){//默認拍成升序if(arr[j]>arr[j+1]){int tmp=arr[j];arr[j]=arr[j+1];arr[j+1]=tmp;}}}
}
void print_arr(int arr[],int sz)
{int i=0;for(i=0;i<sz;i++){printf("%d ",arr[i]);}printf("\n");
}
void test()
{int arr[10]={1,3,5,7,9,2,4,6,8,0};int sz=sizeof(arr)/sizeof(arr[0]);bubble_sort(arr,sz);print_arr(arr,sz);
}
int main()
{test();return 0;
}
那如何知道那些地方需要修改呢?通過與qsort的對比我們可以得出以下幾個地方需要改造:
那我們就先來改造參數部分:void bubble_sort(void*base,int sz,int width,int (*cmp)(const void*e1,const void*e2))
改造之后我們發現多了兩個參數,一個是 width
代表單個元素大小;一個是 int (*cmp)(const void*e1,const void*e2)
這個函數指針。
其中一個修改的地方是從原來接受
int類型
的數組改為了void*類型
原因是方便接受任意類型的數組。
其次我們來修改比較部分,上面分析了使用一個函數指針指向一個函數然后通過函數的返回值來得到e1和e2這兩個元素的大小關系,這也是為什么函數指針的返回類型是int的原因。
int comp(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
比較函數我們依然可以這樣寫但是要注意一個問題,現在是我們模仿qsort的邏輯,所以在bubble_sort這個函數里邊就會涉及一個傳參的問題,下面我們著重來探究傳參問題:
if(arr[j]>arr[j+1])——————>if(comp((char*)base+j*width,(chr*)base+(j+1)*width)>0)
//注意base就是arr
解決了參數問題,解決了傳參問題接下來就是交換數據的問題了,如果不滿足我們的升序要求則需要交換那怎么交換呢?通過上面的比較得出需要交換那我們既然已經得到了要交換的兩個元素的地址,就不妨再封裝一個函數來專門去交換元素的內容。
#include<stdio.h>
#include<stdlib.h>
void swap(char* buf1, char* buf2, int width)
{int i = 0;for (i = 0;i < width;i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
解決完交換的問題我們就可以給出所有的代碼啦:
int bubble_cmp(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
void swap(char* buf1, char* buf2, int width)
{int i = 0;for (i = 0;i < width;i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
int bubble_sort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{int i = 0;for (i = 0;i < sz-1;i++){int j = 0;for (j = 0;j < sz - 1 - i;j++){if (bubble_cmp((char*)base+j*width,(char*)base+(j+1)*width)>0){swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}}}
}
void test2()
{int arr[10] = { 1,3,5,7,9,2,4,6,8,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), bubble_cmp);print(arr, sz);
}
int main()
{test2();//使用冒泡排序來模擬qsortreturn 0;
}
好了以上就是本章的全部內容啦!
感謝能夠看到這里的讀者,如果我的文章能夠幫到你那我甚是榮幸,文章有任何問題都歡迎指出!制作不易還望給一個免費的三連,你們的支持就是我最大的動力!