摘要:讓我們來看一下代碼,首先我們還是冒泡排序一樣,進行了兩次循環(huán),第一次代表排序趟數(shù),第二次代表每趟的排序次數(shù)。這塊的詳細介紹在本篇文章稍前的冒泡排序中也有詳細介紹。
數(shù)組名在除了定義數(shù)組,使用sizeof()函數(shù)和加&之外均代表數(shù)組首元素的地址
int (*p2)[10] = &arr;
我們首先來看看這個代碼,*p2是一個指針,在代碼中p2外的括號不可缺少的,因為[]的優(yōu)先級更高所以如果不加括號,這行代碼就會變成一個指針數(shù)組的定義
int Add(int x, int y){ return x + y;}
假設(shè)我們要用指針來存儲這個加法函數(shù)的地址,首先我們要知道函數(shù)名就代表函數(shù)的地址
int (*pf)(int, int) = Add;//pf是指針名,最前面的int代表函數(shù)的返回類型,后面的兩個int表示函數(shù)的參數(shù)類型
值得注意的是,代表函數(shù)的參數(shù)類型的int后面加上x或y或者任意字符都是可以的,只要能明確其類型就行,但這沒有必要
因為函數(shù)名就代表函數(shù)的地址,所以事實上函數(shù)的指針與函數(shù)名是等價的,所以我們在調(diào)用函數(shù)指針時既可以將函數(shù)指針解引用為原函數(shù)名來使用函數(shù),也可以使用指針來直接調(diào)用,如
// 先解引用為原函數(shù)名int sum = (*pf)(2,3);// 直接使用指針sum = pf(2,3);
我們可以發(fā)現(xiàn)在第一個調(diào)用過程中,*其實只是一個擺設(shè)
顧名思義,該數(shù)組就是用來存放函數(shù)指針的數(shù)組(也可以說數(shù)組中存的就是函數(shù)名),那么我們要如何來使用這個東西呢?現(xiàn)在咱們再增加一個減法函數(shù)
int Sub(int x, int y){ return x-y;}
那么比方說我們想要來完成一個能實現(xiàn)加減法的計算器功能,那么我們可以先進行如下代碼
int (*pA)(int, int) = Add;int (*pS)(int, int) = Sub;
我們會發(fā)現(xiàn)這兩個函數(shù)無論是參數(shù)還是返回值的類型都是相同的,所以我們可以考慮使用一個指針數(shù)組來實現(xiàn)代碼,讓我們這樣定義函數(shù)指針數(shù)組
int (*pArr[5])(int, int) = {Add, Sub};//因為[]的優(yōu)先級高于*,所以當指針名不加括號時pArr就代表一個函數(shù)指針數(shù)組
更為深入地來看,我們可不可以獲取并存儲pArr的地址呢?答案是可以的
int (*(*p)[5])(int, int) = &pArr;//其中p是一個指針指向了一個元素為函數(shù)指針的數(shù)組
到這里套娃的工作我們就不繼續(xù)了,讓我們再來看看回調(diào)函數(shù)
回調(diào)函數(shù)就是用一個函數(shù)來調(diào)用另一個函數(shù),以實現(xiàn)在特定條件下調(diào)用某個函數(shù)的目的?,F(xiàn)在我們有一個Calc()函數(shù),我們要求當我們把Add函數(shù)傳過去時就完成加法,將Sub傳過去時就完成減法,
void Calc(int (*pf)(int, int)){int ret = pf(5,3);printf("%d", ret);}int main(){Calc(Add);//既然我們傳輸了函數(shù)的地址,我們就要用一個函數(shù)指針來接收Calc(Sub); return 0;}
這就是一個簡單的回調(diào)函數(shù),當把Add傳參給Calc時,pf就接收了Add的地址,并用Add來計算5+3的值,Sub同理來計算5-3的值
我們擁有很多種排序方法,如冒泡排序,選擇排序,插入排序等,這次我想主要來講qsort快速排序,并在簡單說完冒泡排序后用冒泡排序來模擬qsort函數(shù)
我們先來簡單地講講冒泡排序,冒泡排序的基本思想無非是兩兩比較大小并排序,,比方說我想把{9,8,7,6,5,4,3,2,1,0}這10個數(shù)字改為升序應(yīng)該怎么做呢
void BubbleSort(int arr[], int sz) { int i = 0; for(i = 0; i < sz - 1; i++)//我們每輪會改變一個數(shù)字的位置,因為當我們把之前的9個數(shù)字排列完之后最后一個數(shù)字的位置也必然已經(jīng)正確了,故只需排列比數(shù)組大小少一輪 { int j = 0; for(j = 0; j <= sz - i -1; j++);//因為每輪過后都會有一個數(shù)字已經(jīng)排序完成且位于數(shù)列后方,所以每輪之后需要排列的數(shù)字都可以減1 { if(arr[j] > arr[j+1]) { int tmp = arr[j+1]; arr[j+1] = arr[j]; arr[j] = tmp; } } }}int main(){ int arr[10] = {9,8,7,6,5,4,3,2,1,0}; BubbleSort(arr,sz);//當我們要進行排序的時候一般會考慮將數(shù)組的大小也傳送過去}
這就是一個冒泡排序,如果想看它的結(jié)果,我們也可以再添加一個打印程序
PrintArr(int arr, int sz){ int i = 0; for(i = 0; i < sz; i++) { printf("%d ", arr[i]); }}
對于冒泡排序,我們會發(fā)現(xiàn)它只能用于對整型的排序,那么如果我們想讓一個程序來排列任意類型時,應(yīng)該怎么辦呢,qsort函數(shù)能幫我們解決這個難題,下面我們來講講qsort函數(shù)
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
這是qsort函數(shù)的格式規(guī)范,看起來很長很復(fù)雜,但當我們把它給細分出來時就顯得很簡單了,讓我們把它分行寫出來,
void qsort( void base,
size_t num,
size_t width,
int (cmp)(const void e1, const void e2 ) );
之前說過,我們排序時除了傳送數(shù)組名外我們還會把數(shù)組大小也給傳送,但在這個函數(shù)中,我們可以發(fā)現(xiàn)這個函數(shù)第一個參數(shù)是數(shù)組,第二個參數(shù)是數(shù)組大小(元素個數(shù)),那么之后兩個參數(shù)是什么東西?
要回答這個問題,我們首先要注意一下base前的類型是void,void是一種無具體類型的指針,可以理解為通用指針,因為我們希望qsort函數(shù)能夠排序任意類型的數(shù)組,所以我們就需要void的幫助
重點來了!
void能接收任意類型的地址,但它有一個缺點:不能進行運算,不能解引用,因為指針進行運算時依賴指針類型來明確指針所指向類型的內(nèi)存大小,比如若p是一個整型指針,那么p++增加的就是4bit,而當p是字符指針時,p++增加的就只是1bit,而void*未明確指針指向的類型,自然無法進行運算了,所以我們就能理解第三個參數(shù)存在的意義了,它代表的是每個元素類型所占的內(nèi)存大小,即寬度,這樣qsort函數(shù)就可以知道每個元素的內(nèi)存大小以便排序的進行了
現(xiàn)在我們?yōu)閝sort函數(shù)傳輸了數(shù)組,元素個數(shù)和元素大小,按道理來說這已經(jīng)足以讓qsort進行排序了,那第四個參數(shù)有什么作用呢?你一定想到了,我們當初使用qsort的目的之一就是為了排序任意類型的元素,所以我們必須對種類型的元素定義一個特定的排序規(guī)則(先提一嘴,第四個參數(shù)就是一個函數(shù)指針,也就是說我們需要創(chuàng)建一個函數(shù)并傳遞它的指針),比方說我現(xiàn)在想要給一個結(jié)構(gòu)體排序,
Struct Stu{ char name[20]; int age; float score;}; struct Stu s[3] = { {"張三",15,70.0},{"李四",35,72.0},{"王五",55,14.0} };
顯然我們不能簡單地比較三個結(jié)構(gòu)體的大小,所以我們必須確立一個規(guī)則,確定應(yīng)該使用名字,年齡還是分數(shù)來對三位學(xué)生進行排序,我們先來看看qsort對這個排序規(guī)則的要求
返回值 | 描述 |
---|---|
返回值小于0 | e1小于e2 |
返回值等于0 | e1等于e2 |
返回值大于0 | e1大于e2 |
也就是說當e1小于e2時,cmp需要返回一個負數(shù)作為qsort的參數(shù),等于時返回0,大于時返回正數(shù),這個很簡單,只需要將e1-e2即可,現(xiàn)在讓我們看看如果想用名字來進行排序應(yīng)該怎么操作
#define _CRT_SECURE_NO_WARNINGS#include #include #include struct Stu{ char name[20]; int age; float score;};int cmp(const void* e1, const void* e2){ return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);//因為void*類型不能進行運算,所以我們把它強制轉(zhuǎn)換為struct Stu*類型,取其中的name,并用strcmp函數(shù)來比較字符串大小}int main(){ struct Stu s[3] = { {"張三",15,70.0},{"李四",35,72.0},{"王五",55,14.0} }; int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp);//這里實際上就是一個回調(diào)函數(shù),qsort回調(diào)了cmp,cmp接收了類型為const void*的指針e1和e2 return 0;}
同理,如果我們想用年齡來進行排序,我們只需將cmp函數(shù)修改為
return ((struct Stu*)e1)->age-((struct Stu*)e2)->age;
而如果我們想實現(xiàn)倒敘則只需要把cmp中前后兩個參數(shù)調(diào)換位置即可。
現(xiàn)在我們已經(jīng)可以理解冒泡排序和qsort的用法,那么如何使用冒泡排序來模擬qsort的功能呢?
現(xiàn)在筆者先把整體代碼展現(xiàn)一下,接著再對每塊代碼細細解釋
void print_arr(int arr[], int sz){ int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("/n");}int 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++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; }}void BubbleSort(void* base, size_t num, size_t width, int (*cmp_int)(const void* e1, const void* e2)){ size_t i = 0; for (i = 0; i < num - 1; i++) { size_t j = 0; for (j = 0; j < num - 1 - i; j++) { if (cmp_int((char*)base + j * width, (char*)base + (j + 1) * width) > 0) { swap((char*)base + j * width, (char*)base + (j + 1) * width, width); } } }}int main(){ int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); BubbleSort(arr, sz, sizeof(arr[0]), cmp_int); print_arr(arr, sz); return 0;}
現(xiàn)在讓我們來理一下這些代碼的思路。
這里的BubbleSort()其實就相當于qsort(),和qsort一樣,我們也傳入了4個參數(shù),分別是數(shù)組,元素個數(shù),元素大小和排序函數(shù),他們的作用也和qsort函數(shù)中的一樣,我就不一一贅述了。讓我們來看一下代碼,首先我們還是冒泡排序一樣,進行了兩次循環(huán),第一次(i)代表排序趟數(shù),第二次(j)代表每趟的排序次數(shù)。(這塊的詳細介紹在本篇文章稍前的冒泡排序中也有詳細介紹。)所以讓我們馬上來看看這個函數(shù)的不同之處,讓我們接著看代碼,
if (cmp_int((char*)base + j * width, (char*)base + (j + 1) * width) > 0) { swap((char*)base + j * width, (char*)base + (j + 1) * width, width); }
在這里我們使用了兩個函數(shù),cmp_int和swap,其中cmp_int的功能是對數(shù)列前后兩個元素進行大小比較,swap則負責對降序的元素位置進行交換。
類似于qsort函數(shù),在這個函數(shù)中我們也希望這個比較函數(shù)能夠在升序時返回負數(shù),降序時返回一個正數(shù),所以我們只需要將數(shù)組中的前后兩個元素依次傳參,并用前一個元素減去后一個元素,并將該值作為函數(shù)的返回值。而其中我們?yōu)榱四軌蚴惯@個比較函數(shù)適用于各種類型的數(shù)組,我們在傳參時將參數(shù)類型設(shè)成了char*,所以在這個為int類型比較大小的函數(shù)里,我們可以把它強制類型轉(zhuǎn)換為int*(當然在字符比較函數(shù)里我們可以將它強制轉(zhuǎn)換為char*)
如果知道這個函數(shù)中有兩個元素是降序,我們就要將其調(diào)換位置,在傳參時,為了函數(shù)的通用性,我們依然傳遞了char類型的參數(shù),并用char進行接收,所以我們絕對就不能直接調(diào)換元素的位置,而具體的調(diào)換方法呢,我們就用9和8這兩個元素代替,我們知道在小端存儲中,9和8的內(nèi)存分別為09 00 00 00和08 00 00 00且他們的內(nèi)存是連續(xù)的
我們想要把09和08,09后的00和08后的00,09后的00后的00······(此處省略一萬字)交換位置,所以現(xiàn)在思路就有了,我們首先獲得09和08元素的第一個字節(jié)的地址,交換位置后向后調(diào)整一個字節(jié),直到這個元素的所有字節(jié)被調(diào)換完,對于int類型,它的大小是四個字節(jié),所以我們需要對每個元素調(diào)換四次位置
void swap(char* buf1, char* buf2, int width){ int i = 0;//i代表調(diào)換次數(shù),這里我們希望他循環(huán)4次 for (i = 0; i < width; i++) { //調(diào)換 char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; }}
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/119810.html
摘要:函數(shù)的返回值為指針就按照字面意思,指針函數(shù)的定義顧名思義,指針函數(shù)即返回指針的函數(shù)。 目錄 前言指針與函數(shù)函數(shù)的返回值為指針作為函數(shù)參數(shù)的指針指針函數(shù)可以改變變量...
摘要:作者陳大魚頭鏈接背景最近高級前端工程師劉小夕在上開了個每個工作日布一個前端相關(guān)題的,懷著學(xué)習的心態(tài)我也參與其中,以下為我的回答,如果有不對的地方,非常歡迎各位指出。 作者:陳大魚頭 github: KRISACHAN 鏈接:github.com/YvetteLau/S… 背景:最近高級前端工程師 劉小夕 在 github 上開了個每個工作日布一個前端相關(guān)題的 repo,懷著學(xué)習的心態(tài)我也參...
摘要:釋放不完全導(dǎo)致內(nèi)存泄漏。既然把柔性數(shù)組放在動態(tài)內(nèi)存管理一章,可見二者有必然的聯(lián)系。包含柔性數(shù)組的結(jié)構(gòu)用進行動態(tài)內(nèi)存分配,且分配的內(nèi)存應(yīng)大于結(jié)構(gòu)大小,以滿足柔性數(shù)組的預(yù)期。使用含柔性數(shù)組的結(jié)構(gòu)體,需配合以等動態(tài)內(nèi)存分配函數(shù)。 ...
摘要:所以是數(shù)組指針,而是指針數(shù)組。因為對一個二維數(shù)組,可以不知道有多少行,但是必須知道一行多少元素。當二維數(shù)組數(shù)組名傳參,形參接收時,數(shù)組的行可以省略,列不能省略,如果省略了列,我們就無法知道當指針加減跳過幾個字節(jié)。 ...
摘要:本章節(jié)在此基礎(chǔ)上,對語言階段指針進行更深層次的研究。數(shù)組指針的類型由數(shù)組類型決定,先找出數(shù)組的類型去掉名就是類型。相當于數(shù)組指針所指向數(shù)組的數(shù)組名。數(shù)組指針指向整個數(shù)組,將其看作二維數(shù)組并解引用得到一行的首元素,從而遍歷訪問。 ...
閱讀 822·2023-04-25 19:28
閱讀 1479·2021-09-10 10:51
閱讀 2472·2019-08-30 15:55
閱讀 3466·2019-08-26 13:55
閱讀 3071·2019-08-26 13:24
閱讀 3385·2019-08-26 11:46
閱讀 2810·2019-08-23 17:10
閱讀 1487·2019-08-23 16:57