- 魔王的介紹:😶?🌫?一名雙非本科大一小白。
- 魔王的目標(biāo):🤯努力趕上周圍卷王的腳步。
- 魔王的主頁(yè):🔥大魔王不哭
我們初識(shí)C語(yǔ)言時(shí),會(huì)做過(guò)讓一個(gè)整型數(shù)組按照從小到大來(lái)排序的問(wèn)題,我們使用的是冒泡排序法,但是如果我們想要比較其他類型怎么辦呢,顯然我們當(dāng)時(shí)的代碼只適用于簡(jiǎn)單的整形排序,對(duì)于結(jié)構(gòu)體等沒(méi)辦法排序,本篇將引入一個(gè)庫(kù)函數(shù)來(lái)實(shí)現(xiàn)我們希望的順序。
#includeint main()
{int arr[10] = {9,8,7,6,5,4,3,2,1,0 };
int i = 0;
int sz = sizeof(arr);
for (i = 0; i< sz-1; i++)
{int j = 0;
for (j = 0; j< sz-1-i; j++)
{ int tep = 0;
if (arr[j] >arr[j + 1])
{ tep = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tep;
}
}
}
return 0;
}
這是我們最初學(xué)習(xí)的冒泡排序法,我們?nèi)绻哑湫薷囊幌?,能不能比較其它類型的數(shù)據(jù)呢,結(jié)果是肯定的。
- 首先我們可以看出qsort函數(shù)不需要返回類型,有四個(gè)參數(shù),分別為空指針、無(wú)符號(hào)型、無(wú)符號(hào)型、函數(shù)指針。
- 圖二說(shuō)明該函數(shù)無(wú)返回類型。
- 圖三說(shuō)明我們base為要排序中的起始地址,是一個(gè)空指針類型,所以我們傳首元素地址。
- 圖四說(shuō)明num為元素個(gè)數(shù),是一個(gè)無(wú)符號(hào)整型,所以我們傳一個(gè)無(wú)符號(hào)整型。
- 圖五說(shuō)明width為每個(gè)元素的寬度,單位為字節(jié),是一個(gè)無(wú)符號(hào)整型,所以我們傳一個(gè)無(wú)符號(hào)整型。
- 圖六說(shuō)明這個(gè)函數(shù)指針指向一個(gè)含有兩個(gè)空指針作為參數(shù)并且返回類型為int的(比較)函數(shù)。這個(gè)是qsort的最后一個(gè)參數(shù),作用是讓我們提供比較大小的規(guī)則,所以我們傳的時(shí)候需要傳一個(gè)函數(shù)地址讓庫(kù)函數(shù)中qsort的函數(shù)指針來(lái)接收。
- 補(bǔ)充:qsort對(duì)應(yīng)的頭文件為
。(比較)函數(shù)中的兩個(gè)參數(shù)如果第一個(gè)大返回一個(gè)大于0的數(shù),如果第二個(gè)大返回一個(gè)小于0的數(shù),如果相等則返回0。
2.1、qsort函數(shù)排序整型數(shù)組在這里我們舉出兩個(gè)例子來(lái)直觀看出qsort的應(yīng)用方法,分別為整型數(shù)組排序和結(jié)構(gòu)體排序。
#include#includeint cmp_int(const void* e1, const void* e2)
{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
2.2、qsort函數(shù)排序結(jié)構(gòu)體疑問(wèn):
1.qsort第四個(gè)參數(shù)(函數(shù)地址)為什么不需要傳參?
首先:我們要知道,我們第四個(gè)參數(shù)傳的是一個(gè)函數(shù)地址(函數(shù)名加不加取地址符號(hào)都可以表示函數(shù)的地址),而不是一個(gè)函數(shù),所以就沒(méi)有傳參這個(gè)說(shuō)法。其次:等到庫(kù)函數(shù)qsort用函數(shù)指針接收后會(huì)進(jìn)行傳參等工作(這是程序內(nèi)部進(jìn)行的,我們不需要管)。
2.為什么函數(shù)定義里面的參數(shù)是空指針類型,而不是特定類型?
因?yàn)閝sort函數(shù)并不知道你要傳的是什么類型,也就是它使用這個(gè)函數(shù)時(shí)不知道傳什么類型的實(shí)參,所以統(tǒng)一先傳一個(gè)空類型地址然后函數(shù)用空類型指針接收。
3.空指針是什么意思,怎么用?
空指針就像一個(gè)垃圾桶,我們可以對(duì)其賦上任意類型的指針,這很方便,但是它不能直接使用,就像我們賦的時(shí)候一樣,用的時(shí)候它并不知道你是什么類型,它相當(dāng)于只儲(chǔ)存了一個(gè)字節(jié)的地址,沒(méi)有明確的步長(zhǎng)(指針+1跳過(guò)的字節(jié))和訪問(wèn)權(quán)限大小(解引用指針訪問(wèn)的字節(jié)個(gè)數(shù))。所以當(dāng)我們用空指針時(shí),需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換,轉(zhuǎn)換成我們需要的類型。
4.為什么com_int函數(shù)在用的時(shí)候要把參數(shù)強(qiáng)轉(zhuǎn)為int?
因?yàn)楫?dāng)我們使用qsort函數(shù)時(shí),我們肯定知道要進(jìn)行排序的類型,所以我們需要根據(jù)自己的需要的類型來(lái)寫出對(duì)應(yīng)的函數(shù)讓qsort函數(shù)調(diào)用。
#include#include#includestruct stu
{char name[10];
int age;
};
//排序結(jié)構(gòu)體年齡的回調(diào)函數(shù)
int cmp_struct_stu_age(const void*e1,const void*e2)
{return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}
//排序結(jié)構(gòu)體姓名的回調(diào)函數(shù)
int cmp_struct_stu_name(const void* e1, const void* e2)
{return strcmp(((struct stu*)e1)->name, ((struct stu*)e1)->name);
}
//運(yùn)用qsort函數(shù)排序結(jié)構(gòu)體年齡
void test1()
{struct stu s[3] = {{"xiaoming",30},{"lihua",60},{"wangli",40} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_struct_stu_age);
}
//運(yùn)用qsort函數(shù)排序結(jié)構(gòu)體名字
void test2()
{struct stu s[3] = {{"xiaoming",30},{"lihua",60},{"wangli",40} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_struct_stu_name);
}
int main()
{test1();
//test2();
return 0;
}
你對(duì)于qsort內(nèi)部如何執(zhí)行操作的是否感興趣呢?你對(duì)于qsort中傳的那些參數(shù)是否存在疑問(wèn),不知道他們有什么作用呢,接著看下面的內(nèi)容,讓我們更進(jìn)一步理解qsort的工作原理,同時(shí)讓你的冒泡排序變得具有普遍性。
#includeint cmp_int(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++, buf1++, buf2++)
{char tep = 0;
tep = *buf1;
*buf1 = *buf2;
*buf2 = tep;
}
}
void my_qsort(const 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(cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
void test3()
{int arr[10] = {9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
my_qsort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i< sz; i++)
printf("%d ", arr[i]);
}
int main()
{test3();
return 0;
}
以上代碼就是我們模擬出的qsort函數(shù),相信看完這個(gè)你會(huì)對(duì)qsort理解更深刻,不過(guò)有一點(diǎn)需要注意,我們模擬時(shí)使用的是冒泡排序,而qsort其實(shí)內(nèi)部使用的是快排,所以其實(shí)稍有差距。但大體是一致的。
補(bǔ)充:
1.在my_qsort函數(shù)中,我們?cè)诒容^函數(shù)(cmp)中傳入的是內(nèi)容解釋:
??(char*)base+jwidth 這個(gè)實(shí)際上就是原始的base是空指針類型,所以我們無(wú)法直接使用,但我們也不確定傳來(lái)的具體是什么類型,因此我們先將其強(qiáng)制類型轉(zhuǎn)換為char,然后根據(jù)指針加幾就跳過(guò)幾個(gè)步長(zhǎng)個(gè)字節(jié)來(lái)確定每次跳過(guò)一個(gè)元素。
2.在my_qsort函數(shù)中,我們的swap函數(shù)也是一個(gè)重要部分,它是如何實(shí)現(xiàn)的呢?
??首先和1剛開(kāi)始一樣,我們傳的實(shí)參的形式就是為了保證每次可以跳過(guò)一個(gè)元素,然后當(dāng)傳到swap函數(shù)里后,我們不像cmp函數(shù)一樣有提前準(zhǔn)備好的強(qiáng)轉(zhuǎn)類型,因此我們需要用一種特別的方法保證可以互換元素,那么我們會(huì)想到元素是怎樣儲(chǔ)存的呢,是以二進(jìn)制以字節(jié)為單位儲(chǔ)存的,那么我們?nèi)绻枰粨Q元素,是不是只需要把每個(gè)字節(jié)都交換一次就好了呢?答案是肯定的,于是我們也將傳來(lái)的地址強(qiáng)制類型轉(zhuǎn)換為char*,保證每次交換的是一個(gè)字節(jié),然后又有一個(gè)問(wèn)題就是我們需要交換幾次呢,還記得之前傳的那個(gè)參數(shù)width嗎,它是每個(gè)元素的寬度,單位是字節(jié),因此我們可以通過(guò)一個(gè)for循環(huán),修女換width次來(lái)使得一個(gè)元素所占的字節(jié)全部交換。
我們這一部分雖然是用模擬的qsort來(lái)排序整型數(shù)組,但其實(shí)如果這個(gè)學(xué)會(huì)了的話,結(jié)構(gòu)體和前面說(shuō)的是一模一樣的,因?yàn)槟M的qsort不用變,只需要改一下cmp函數(shù)的類型即可。
#include#include#include//排序整型數(shù)組順序時(shí)的回調(diào)函數(shù)
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
//聲明結(jié)構(gòu)體類型
struct stu
{char name[10];
int age;
};
//排序結(jié)構(gòu)體年齡順序時(shí)的回調(diào)函數(shù)
int cmp_struct_stu_age(const void*e1,const void*e2)
{return ((struct stu*)e1)->age - ((struct stu*)e2)->age;
}
//排序結(jié)構(gòu)體名字順序時(shí)的回調(diào)函數(shù)
int cmp_struct_stu_name(const void* e1, const void* e2)
{return strcmp(((struct stu*)e1)->name, ((struct stu*)e1)->name);
}
//運(yùn)用qsort函數(shù)排序整型數(shù)組
void test1()
{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);
}
//運(yùn)用qsort函數(shù)排序結(jié)構(gòu)體
void test2()
{struct stu s[3] = {{"xiaoming",30},{"lihua",60},{"wangli",40} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_struct_stu_name);
//qsort(s, sz, sizeof(s[0]), cmp_struct_stu_age);
}
//模擬qsort中的交換函數(shù)
void swap(char* buf1, char* buf2, int width)
{int i = 0;
for (i = 0; i< width; i++, buf1++, buf2++)
{char tep = 0;
tep = *buf1;
*buf1 = *buf2;
*buf2 = tep;
}
}
//模擬qsort
void my_qsort(const 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(cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
}
}
}
//運(yùn)用模擬qsort函數(shù)(my_qsort)排序整型數(shù)組
void test3()
{int arr[10] = {9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
my_qsort(arr, sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i< sz; i++)
printf("%d ", arr[i]);
}
//運(yùn)用模擬qsort函數(shù)(my_qsort)排序結(jié)構(gòu)體
void test4()
{struct stu s[3] = {{"xiaoming",30},{"lihua",60},{"wangli",40} };
int sz = sizeof(s) / sizeof(s[0]);
my_qsort(s, sz, sizeof(s[0]), cmp_struct_stu_name);
//qsort(s, sz, sizeof(s[0]), cmp_struct_stu_age);
}
int main()
{//test1(); //用qsort實(shí)現(xiàn)整型數(shù)組排序
//test2(); //用qsort實(shí)現(xiàn)結(jié)構(gòu)體排序
test3(); //用模擬qsort函數(shù)實(shí)現(xiàn)整型數(shù)組排序
//test(4) //用模擬qsort函數(shù)實(shí)現(xiàn)結(jié)構(gòu)體排序
return 0;
}
五、總結(jié)回調(diào)函數(shù):回調(diào)函數(shù)就是一個(gè)通過(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)。
通常來(lái)講就是:把這個(gè)函數(shù)地址作為參數(shù)傳過(guò)去,另一個(gè)函數(shù)用函數(shù)指針接收然后再調(diào)用這個(gè)函數(shù),那么這個(gè)被調(diào)用的函數(shù)就叫回調(diào)函數(shù)。(通過(guò)函數(shù)指針調(diào)用的這個(gè)函數(shù),叫做回調(diào)函數(shù))
這是第一篇博客,自己以后的博客也一定好好去寫,希望大佬可以給個(gè)點(diǎn)贊關(guān)注加收藏,長(zhǎng)期更新,每周至少三篇。
🔥🔥🔥🔥🔥🔥🔥🔥大魔王不哭🔥🔥🔥🔥🔥🔥🔥
求你了,大魔王真的需要你的關(guān)注!?。?/font>
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧