C語(yǔ)言中回調(diào)函數(shù)和qsort函數(shù)的用法詳解
回調(diào)函數(shù)
通過(guò)函數(shù)指針調(diào)用的函數(shù),如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來(lái)調(diào)用其所指向的函數(shù)時(shí),我們就說(shuō)這是回調(diào)函數(shù)。
回調(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時(shí)由另外的一方調(diào)用的,用于對(duì)該事件或條件進(jìn)行響應(yīng)。
舉例:
#include<stdio.h> void menu() { printf("********************************\n"); printf("** 1.and 2.sub **\n"); printf("** 3.mul 4.div **\n"); printf("********************************\n"); } int add(int x, int y) { int z = 0; z = x + y; return z; } int sub(int x, int y) { int z = 0; z = x - y; return z; } int mul(int x, int y) { int z = 0; z = x * y; return z; } int div(int x, int y) { int z = 0; z = x / y; return z; } void Calc(int(*pf)(int, int))//int(*pf)(int, int)等于add,只不過(guò)選用不同的方式進(jìn)行調(diào)用 { int x = 0; int y = 0; printf("請(qǐng)輸入兩個(gè)操作數(shù):>"); scanf_s("%d%d", &x, &y); printf("%d\n", pf(x, y));//通過(guò)指針對(duì)add函數(shù)進(jìn)行調(diào)用,而不是像之前那樣使用函數(shù)名進(jìn)行調(diào)用 } int main() { int input = 0; do { menu(); printf("請(qǐng)選擇:>"); scanf_s("%d", &input); switch (input) { case 1: Calc(add);//將add函數(shù)的地址傳遞過(guò)去,這里的add函數(shù)為回調(diào)函數(shù) break; case 2: Calc(sub); case 3: Calc(mul); case 4: Calc(div); } } while (input); }
指向函數(shù)指針數(shù)組的指針
指向函數(shù)指針數(shù)組的指針是一個(gè)指針,指向一個(gè)數(shù)組,數(shù)組的元素都是函數(shù)指針;
如何定義?
int arr[10] = { 0 };//整型數(shù)組 int(*p)[10] = &arr;//取出數(shù)組的地址 int (*pf)(int, int);//函數(shù)指針 int(*pfarr[4])(int, int);//pfarr是一個(gè)數(shù)組,函數(shù)指針的數(shù)組 int(*(*ppfarr)[4])(int, int) = &pfarr;//ppfarr指向函數(shù)指針數(shù)組的指針 //pfarr是一個(gè)數(shù)組指針,指針指向的數(shù)組有4個(gè)元素 //指向的數(shù)組的每個(gè)元素的類型是函數(shù)指針int(*)(int,int)
void*
可以用來(lái)接收任何類型數(shù)據(jù)的地址,別名萬(wàn)能指針
既然可以存放任何類型的地址,那么是不是也可以解引用訪問(wèn)存放的值?
下面我們通過(guò)示例:
#include<stdio.h> int main() { int a = 10; void* p = &a; printf("%d\n", *p); }
通過(guò)輸出結(jié)果我們發(fā)現(xiàn),程序并沒(méi)有被正確的運(yùn)行,而是告訴我們,我們進(jìn)行了非法間接尋址。
那么為什么會(huì)出現(xiàn)這樣的現(xiàn)象呢?
原因是void*雖然可以接受任意類型的地址,但它自己本身的類型是空類型,那么在解引用操作的時(shí)候,系統(tǒng)并不知道它的類型,因此不知道需要分配給其幾個(gè)字節(jié),指針類型決定了它的字節(jié)大小。
因此,void*不能進(jìn)行解引用操作
那么可以進(jìn)行++/–操作嗎?
我們通過(guò)實(shí)例進(jìn)行驗(yàn)證一下:
#include<stdio.h> int main() { int a = 10; void* p = &a; p++; }
程序依然沒(méi)有正確運(yùn)行,編譯器指出了錯(cuò)誤的原因:void未知的大小,和上面一樣的道理,我們并不清楚此時(shí)存放在void里面的數(shù)據(jù)是什么類型,自然也不知道它所占據(jù)內(nèi)存空間的大小,因此步長(zhǎng)是無(wú)法確定的。
因此void*也不可以進(jìn)行++/–操作
qsort(qulick sort)-庫(kù)函數(shù)
適用的場(chǎng)景:適用于對(duì)某一組數(shù)據(jù)進(jìn)行快速排序
qsort(s,sz,sizeof(s[0]),cmp_stu_by_name); //第一個(gè)參數(shù)s:待排序數(shù)組的首元素地址 //第二個(gè)參數(shù)sz:待排序數(shù)組的元素個(gè)數(shù) //第三個(gè)參數(shù)sizeof(s[0]):待排序數(shù)組的每個(gè)元素的大小,單位是字節(jié) //第四個(gè)參數(shù):是函數(shù)指針,比較兩個(gè)元素的所用函數(shù)的地址 //這個(gè)函數(shù)使用者自己實(shí)現(xiàn)函數(shù)指針的兩個(gè)參數(shù)是:待比較的兩個(gè)元素的地址
舉例:
常規(guī)方法:冒泡排序:
#include<stdio.h> int main() { int arr[10] = { 9,2,3,1,4,5,7,6,0,91 }; int i, j=0,temp; int sz = sizeof(arr) / sizeof(arr[0]); for (i = 0; i < sz-1; i++)//決定需要比較多少次 { for (j=0; j < sz-1-i; j++) { if (arr[j+1] > arr[j]) { temp = arr[j+1]; arr[j+1] = arr[j]; arr[j] = temp; } } } for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
輸出:
91 9 7 6 5 4 3 2 1 0
但是,這種方法的局限性非常大,執(zhí)行效率也不高。
因此在進(jìn)行數(shù)據(jù)類型的排序問(wèn)題時(shí),我們可以選擇qsort函數(shù):
下面我們就來(lái)學(xué)習(xí)qsort函數(shù):
//int (*cmp)(const void *,const void *); qsort(*s, n, sizeof(s[0]), cmp);
其中第一個(gè)參數(shù)s是一個(gè)地址,即參與排序的首地址; n是需要排序的數(shù)量; sizeof(s[0])則是每一個(gè)元素占用的空間大?。?/p>
指向函數(shù)的指針,用于確定排序的順序。
sz=sizeof(arr)/sizeof(arr[0]) qsort(a, sz,arr[0], cmp); //其中cmp函數(shù)應(yīng)寫(xiě)為: int cmp(const void *a, const void *b)//void*可接受任意類型的數(shù)據(jù) { return *(int*)a - *(int*)b; //由小到大排序 //return *(int *)b - *(int *)a; 由大到小排序 }
對(duì)于整形數(shù)據(jù)的比較實(shí)現(xiàn)過(guò)程:
#include<stdio.h> #include<stdlib.h>#qsort的頭文件 int cmp_int(const void* e1, const void* e2)//e1和e2是用來(lái)接收要比較的兩個(gè)元素的地址 { return *(int*)e1 - *(int*)e2; } int main() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr,sz, sizeof(arr[0]), cmp_int); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]);//整形打印%d } return 0; }
輸出:
0 1 2 3 4 5 6 7 8 9
對(duì)于浮點(diǎn)型數(shù)據(jù)的比較實(shí)現(xiàn)過(guò)程:
int cmp_float(const void* e1, const void* e2)//e1和e2是用來(lái)接收要比較的兩個(gè)元素的地址 //由于cmp_float函數(shù)的返回類型是int,因此,需要進(jìn)行轉(zhuǎn)化 { //可用if分支語(yǔ)句 /*if (*(float*)e1 == *(float*)e2) return 0; else if (*(float*)e1 > *(float*)e2) return 1; else return 0;*/ //也可用return直接返回 return ((int) * (float*)e1 - *(float*)e2); } int main() { float f[] = { 9.0,8.0,7.0,6.0,5.0,4.0 }; int sz = sizeof(f) / sizeof(f[0]); qsort(f,sz, sizeof(f[0]), cmp_float); int i = 0; for (i = 0; i < sz; i++) { printf("%f ", f[i]);//浮點(diǎn)型打印%f } return 0; }
對(duì)于結(jié)構(gòu)體類型數(shù)據(jù)的實(shí)現(xiàn)過(guò)程:
結(jié)構(gòu)體類型和整形浮點(diǎn)型在排序的時(shí)候有略微區(qū)別,結(jié)構(gòu)體類型并不能直接進(jìn)行比較,而要按照某一變量,例如:名字,年齡等等
按照年齡比較:
#include<stdio.h> #include<stdlib.h> struct stu//定義一個(gè)結(jié)構(gòu)體 { char name[20]; int age; }; int cmp_s_stu(const void* e1, const void* e2)//e1和e2是用來(lái)接收要比較的兩個(gè)元素的地址 { return ((struct stu*)e1)->age - ((struct stu*)e2)->age;//告訴編譯器你想用什么樣的方式進(jìn)行排序 } int main() { struct stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} }; int sz = sizeof(s) / sizeof(s[0]); qsort(s,sz, sizeof(s[0]), cmp_s_stu); int i = 0; for (i = 0; i < sz; i++) { printf("%s ", s[i]);#字符串型,以%s進(jìn)行打印 } return 0; }
按照名字進(jìn)行比較:
#include<stdio.h> #include<stdlib.h> #include<string.h> struct stu { char name[20]; int age; }; int cmp_s_stu(const void* e1, const void* e2)//e1和e2是用來(lái)接收要比較的兩個(gè)元素的地址 { return strcmp(((struct stu*)e1)->name,((struct stu*)e2)->name);//比較名字就是比較字符串 //注意字符串在比較大小的時(shí)候,不能直接用加減法進(jìn)行比較,而要用strcmp()函數(shù) } int main() { struct stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} }; int sz = sizeof(s) / sizeof(s[0]); qsort(s,sz, sizeof(s[0]), cmp_s_stu); int i = 0; for (i = 0; i < sz; i++) { printf("%s ", s[i]);#字符串型,以%s進(jìn)行打印 } return 0; }
到此這篇關(guān)于C語(yǔ)言中回調(diào)函數(shù)和qsort函數(shù)的用法詳解的文章就介紹到這了,更多相關(guān)C語(yǔ)言 回調(diào)函數(shù) qsort內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入理解goto語(yǔ)句的替代實(shí)現(xiàn)方式分析
本篇文章是對(duì)goto語(yǔ)句的替代實(shí)現(xiàn)方式進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++基于QWidget和QLabel實(shí)現(xiàn)圖片縮放,拉伸與拖拽
這篇文章主要為大家詳細(xì)介紹了C++如何基于QWidget和QLabel實(shí)現(xiàn)圖片縮放、拉伸與拖拽等功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02C語(yǔ)言程序如何求學(xué)生總成績(jī)和平均成績(jī)
這篇文章主要介紹了C語(yǔ)言程序如何求學(xué)生總成績(jī)和平均成績(jī),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11求素?cái)?shù),用vector存儲(chǔ)的實(shí)現(xiàn)方法
本篇文章是對(duì)求素?cái)?shù),用vector存儲(chǔ)的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++ new與malloc和delete及free動(dòng)態(tài)內(nèi)存管理及區(qū)別介紹
這篇文章主要介紹了深入理解C++中的new/delete和malloc/free動(dòng)態(tài)內(nèi)存管理,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12C++使用string的大數(shù)乘法運(yùn)算(3)
這篇文章主要為大家詳細(xì)介紹了C++使用string的大數(shù)乘法運(yùn)算,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09