中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

體現(xiàn)網(wǎng)站特色全球熱門網(wǎng)站排名

體現(xiàn)網(wǎng)站特色,全球熱門網(wǎng)站排名,多用戶小程序系統(tǒng)開發(fā),wordpress 修改gravatar方法🌈write in front :🔍個(gè)人主頁 : 啊森要自信的主頁 ??真正相信奇跡的家伙,本身和奇跡一樣了不起啊! 歡迎大家關(guān)注🔍點(diǎn)贊👍收藏??留言📝>希望看完我的文章對(duì)你有小小的幫助&am…
  🌈write in front :

🔍個(gè)人主頁 : @啊森要自信的主頁

??真正相信奇跡的家伙,本身和奇跡一樣了不起啊!

歡迎大家關(guān)注🔍點(diǎn)贊👍收藏??留言📝>希望看完我的文章對(duì)你有小小的幫助,如有錯(cuò)誤,可以指出,讓我們一起探討學(xué)習(xí)交流,一起加油鴨。 請(qǐng)?zhí)砑訄D片描述

文章目錄

  • 前言
  • 一、轉(zhuǎn)移表
  • 二、回調(diào)函數(shù)是什么?
  • 三、qsort函數(shù)細(xì)解
    • 3.1 類比冒泡排序?
    • 3.2 qosrt函數(shù)超詳解
    • 3.2.1qsort函數(shù)排序整型數(shù)據(jù)
    • 3.2.2 使?qsort排序結(jié)構(gòu)數(shù)據(jù)
  • 四、 qsort函數(shù)的模擬實(shí)現(xiàn)
    • 4.1 模擬qsort整形數(shù)據(jù)
    • 4.2 模擬`qsort`排序結(jié)構(gòu)數(shù)據(jù)
  • 總結(jié)


前言

本小節(jié),我們將繼續(xù)學(xué)習(xí)C語言轉(zhuǎn)移表,什么是回調(diào)函數(shù),回調(diào)函數(shù)又是什么?qsort函數(shù)怎么使用,怎么理解處理,要注意的細(xì)節(jié),當(dāng)然qsort使用舉例,最后我們進(jìn)行qsort函數(shù)的模擬實(shí)現(xiàn)!文章干貨滿滿,走起!


一、轉(zhuǎn)移表

C語言轉(zhuǎn)移表是指根據(jù)一定條件,實(shí)現(xiàn)程序執(zhí)行流程的跳轉(zhuǎn)或轉(zhuǎn)移的機(jī)制。

具體來說,C語言中實(shí)現(xiàn)轉(zhuǎn)移表的主要方式有:

  1. goto語句:
    goto語句可以實(shí)現(xiàn)無條件跳轉(zhuǎn),直接跳轉(zhuǎn)到指定標(biāo)簽所在的代碼塊
goto 標(biāo)簽名; 

例如:

goto label;
  1. switch語句:
    switch語句根據(jù)表達(dá)式的值,選擇性地執(zhí)行一個(gè)代碼塊。它實(shí)現(xiàn)了有條件跳轉(zhuǎn)。
switch(表達(dá)式)
{case 常數(shù)表達(dá)式1://語句break;case 常數(shù)表達(dá)式2:  //語句break;//其他casedefault://語句
}
  1. continue語句:
    continue 用于跳過循環(huán)體剩余部分,直接跳轉(zhuǎn)到循環(huán)條件判斷語句。

例如:

for(i=0;i<10;i++)
{if(i==5)continue;printf("%d",i);
}
  1. break語句:
    break 用于跳出整個(gè)循環(huán)或switch語句。

例如:

for(i=0;i<10;i++)
{if(i==5)break;printf("%d",i);
} 
  1. return語句:
    return 用于從函數(shù)中返回。

例如:

int func()
{return 0;
}
  1. 拓展:longjmp()/setjmp():

setjmp()longjmp()是C語言中的兩個(gè)非常重要的函數(shù),它們可以實(shí)現(xiàn)非局部跳轉(zhuǎn)的功能。

  • setjmp()函數(shù)聲明如下:
  int setjmp(jmp_buf env);
  • jmp_buf是可以保存環(huán)境信息的結(jié)構(gòu)體。

  • setjmp()會(huì)將當(dāng)前函數(shù)的執(zhí)行環(huán)境信息保存到env中,并返回0。

  • 然后程序可以正常執(zhí)行。

  • 當(dāng)需要跳轉(zhuǎn)時(shí),調(diào)用longjmp(env, val);

    longjmp()函數(shù)聲明如下:

 void longjmp(jmp_buf env, int val);
  • longjmp()第一個(gè)參數(shù)就是setjmp()保存的env。

  • 它會(huì)將程序跳轉(zhuǎn)回setjmp()后面要執(zhí)行的代碼。

  • 但此時(shí)setjmp()會(huì)返回longjmp()第二個(gè)參數(shù)val,而不是0。
    jmp_buf env是setjmp和longjmp函數(shù)用來保存環(huán)境信息的結(jié)構(gòu)體變量。

jmp_buf是一個(gè)預(yù)定義的數(shù)據(jù)類型,它用來描述一個(gè)環(huán)境的狀態(tài)。

  • env是一個(gè)jmp_buf類型的變量。
  • 當(dāng)調(diào)用setjmp(env)時(shí),setjmp函數(shù)會(huì)將當(dāng)前函數(shù)調(diào)用棧(包括函數(shù)參數(shù)、局部變量等環(huán)境信息)保存到env這個(gè)結(jié)構(gòu)體變量中。
  • 之后程序可以正常執(zhí)行。
  • 當(dāng)需要非局部跳轉(zhuǎn)時(shí),調(diào)用longjmp(env, val)longjmp函數(shù)第一個(gè)參數(shù)就是這個(gè)env。
  • longjmp通過env這個(gè)結(jié)構(gòu)體,可以恢復(fù)到setjmp函數(shù)保存環(huán)境時(shí)的狀態(tài)。實(shí)現(xiàn)一個(gè)“跳回”的效果。

小總結(jié):

  • jmp_buf是一個(gè)結(jié)構(gòu)體類型,它可以保存一個(gè)函數(shù)環(huán)境的狀態(tài)信息。
  • env是一個(gè)此類型的變量,用于在setjmplongjmp之間傳遞環(huán)境信息。
  • setjmp函數(shù)把當(dāng)前環(huán)境信息保存到env中。
  • longjmp函數(shù)通過env這個(gè)結(jié)構(gòu)體,實(shí)現(xiàn)恢復(fù)到setjmp時(shí)的環(huán)境狀態(tài),從而實(shí)現(xiàn)非局部跳轉(zhuǎn)。

哎!當(dāng)然你可以把env可以看作是一個(gè)“傳送令牌”,只要通過longjmp把令牌改了,他就重新傳送到setjmp,然后繼續(xù)執(zhí)行,它連接setjmp和longjmp,使得longjmp能找到正確的環(huán)境信息進(jìn)行跳轉(zhuǎn)。

所以通過setjmp()/longjmp()就實(shí)現(xiàn)了一個(gè)非局部跳轉(zhuǎn):程序似乎"跳回"到setjmp()后面要執(zhí)行的代碼,但實(shí)際上環(huán)境已經(jīng)發(fā)生了變化。這在異常處理等場(chǎng)景中很有用。
工作原理是:

  1. setjmp()函數(shù)會(huì)保存當(dāng)前函數(shù)調(diào)用棧(包括函數(shù)參數(shù)和局部變量等信息)的環(huán)境,并返回0。

  2. 之后程序可以正常執(zhí)行。

  3. 當(dāng)需要非局部跳轉(zhuǎn)時(shí),調(diào)用longjmp(),并將在setjmp()保存的環(huán)境作為參數(shù)傳入。

  4. 這個(gè)時(shí)候程序就會(huì)跳轉(zhuǎn)回setjmp()保存的環(huán)境,仿佛從setjmp()后面繼續(xù)執(zhí)行。但此時(shí)setjmp()會(huì)返回非0值。

舉個(gè)例子

# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <setjmp.h>jmp_buf env; //jmp_buf是一個(gè)預(yù)定義的數(shù)據(jù)類型,它用來描述一個(gè)環(huán)境的狀態(tài)。//env是一個(gè)jmp_buf類型的變量。void func()
{//設(shè)置跳轉(zhuǎn)點(diǎn)int ret = setjmp(env);if (0 == ret){//正常流程printf("In func()\n");//觸發(fā)跳轉(zhuǎn)longjmp(env, 1);}else{//跳轉(zhuǎn)后流程printf("Jumped back to func()\n");}
}int main()
{func();return 0;
}

在這里插入圖片描述

程序執(zhí)行流程:

  1. 主函數(shù)調(diào)用func()函數(shù)。
  2. func()內(nèi)首先調(diào)用setjmp()設(shè)置跳轉(zhuǎn)點(diǎn)env。由于setjmp()第一次調(diào)用會(huì)返回0,所以進(jìn)入if塊。
  3. 打印"In func()"信息。
  4. 調(diào)用longjmp(),觸發(fā)非局部跳轉(zhuǎn)。
  5. 程序跳轉(zhuǎn)回setjmp()設(shè)置的環(huán)境env,此時(shí)setjmp()返回1。
  6. 執(zhí)行else塊內(nèi)的代碼,打印"Jumped back to func()"。
  7. func()返回,主函數(shù)結(jié)束。

通過在函數(shù)內(nèi)使用setjmp()/longjmp(),實(shí)現(xiàn)了從函數(shù)內(nèi)非局部跳回函數(shù)外的功能。這與goto不同,可以實(shí)現(xiàn)跨函數(shù)的非順序跳轉(zhuǎn)。它常用于異常和錯(cuò)誤處理等場(chǎng)景。

  1. C語言函數(shù)指針數(shù)組可以用來實(shí)現(xiàn)轉(zhuǎn)移表。

具體來說:

  • 定義一個(gè)函數(shù)指針數(shù)組,元素類型為函數(shù)指針。

  • 每個(gè)數(shù)組元素都指向一個(gè)具體的函數(shù)。

  • 根據(jù)條件調(diào)用數(shù)組對(duì)應(yīng)元素所指向的函數(shù)。

這與傳統(tǒng)的switch語句實(shí)現(xiàn)轉(zhuǎn)移的效果是一致的。

一個(gè)簡(jiǎn)單的示例:

// 函數(shù)定義
void func1() 
{printf("func1\n");
}void func2() 
{printf("func2\n"); 
}// 主函數(shù)
int main() 
{// 函數(shù)指針數(shù)組void (*funcs[])(void) = {func1, func2};int id = 1; // 條件值// 根據(jù)條件調(diào)用數(shù)組元素函數(shù)funcs[id]();return 0;
}

在這里插入圖片描述

這樣就實(shí)現(xiàn)了根據(jù)條件值動(dòng)態(tài)調(diào)用不同函數(shù)的功能,相當(dāng)于一個(gè)簡(jiǎn)單的轉(zhuǎn)移表。

函數(shù)指針數(shù)組用于轉(zhuǎn)移表的優(yōu)點(diǎn)是:

  • 更靈活,可以在運(yùn)行時(shí)動(dòng)態(tài)添加/刪除函數(shù)
  • 擴(kuò)展性好,支持條件復(fù)雜情況下的多路徑轉(zhuǎn)移
  • 與傳統(tǒng)switch語句相比代碼更簡(jiǎn)潔清晰

所以總的來說,函數(shù)指針數(shù)組正是C語言實(shí)現(xiàn)轉(zhuǎn)移表的一個(gè)很好的選擇。它可以很好地替代switch語句實(shí)現(xiàn)更復(fù)雜的多路轉(zhuǎn)移邏輯。
通過這個(gè)你可能不太能看出哪里能很好的替代switch語句,讓我們來看一個(gè)典型的例子,實(shí)現(xiàn)一個(gè)計(jì)算器!!!

switch實(shí)現(xiàn)計(jì)算器:

主要實(shí)現(xiàn)計(jì)算器程序思路:

  1. 定義了四個(gè)運(yùn)算函數(shù)add、sub、mul、div實(shí)現(xiàn)四則運(yùn)算。

  2. main函數(shù)中:

  • 使用do while循環(huán)控制程序循環(huán)執(zhí)行。

  • 打印菜單讓用戶選擇運(yùn)算類型。

  • 根據(jù)用戶選擇用switch case調(diào)用對(duì)應(yīng)的運(yùn)算函數(shù)。

  • 每次運(yùn)算前輸入兩個(gè)操作數(shù),運(yùn)算后打印結(jié)果。

  1. 選擇0退出循環(huán),退出程序。
#include <stdio.h>int add(int a, int b)//加法
{return a + b;
}int sub(int a, int b)//減法
{return a - b;
}int mul(int a, int b)//乘法
{return a * b;
}int div(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("****  3:mul   4:div   ***\n");printf("****  0:exit   ....   ***\n");printf("*************************\n");printf("請(qǐng)選擇:");scanf("%d", &input);switch (input)//選擇{case 1:printf("輸入兩個(gè)操作數(shù):");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("輸入兩個(gè)操作數(shù):");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("輸入兩個(gè)操作數(shù):");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("輸入兩個(gè)操作數(shù):");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出程序\n");break;default:printf("選擇錯(cuò)誤,請(qǐng)重新選擇\n");break;}} while (input);return 0;
}

在這里插入圖片描述

實(shí)現(xiàn)是實(shí)現(xiàn)了,但是case里面的每個(gè)代碼塊除了ret = (?)(x,y);有點(diǎn)不同,其他都很相似,這么多代碼重復(fù)寫,會(huì)造成代碼的冗余,如果我們又繼續(xù)給用戶增加功能,比如&,^,>>等等,然后一個(gè)功能我們就加一個(gè)case,case多了,代碼重復(fù)的也多咋改變呢?

不著急,我們不是學(xué)習(xí)了函數(shù)指針數(shù)組嗎?

我們可以把函數(shù)的地址存儲(chǔ)在數(shù)組里面,然后通過指針訪問數(shù)組下標(biāo)(0,1,2,3,4,5...),然后解引用找到我們要找到我們要實(shí)現(xiàn)函數(shù)的地址
然后給他傳參,再接收他的計(jì)算的返回值不就搞定了。
哈哈哈哈!!掌聲應(yīng)該送給自己,說做就做!讓我們繼續(xù)往下走。
在這里插入圖片描述

函數(shù)指針數(shù)組實(shí)現(xiàn)計(jì)算器:

思路:

  1. 定義了4個(gè)函數(shù)Add、Sub、Mul、Div,用于四則運(yùn)算。

  2. menu()函數(shù)打印菜單界面。

  3. 定義了一個(gè)函數(shù)指針數(shù)組pfArr,元素類型為int (*)(int, int),可以存儲(chǔ)這4個(gè)二元運(yùn)算函數(shù)的地址。

  4. 在主函數(shù)中使用do-while循環(huán)不斷運(yùn)行:

    • 調(diào)用menu()打印菜單

    • scanf輸入選擇

    • 根據(jù)選擇從pfArr數(shù)組中獲取對(duì)應(yīng)函數(shù)的地址

    • 調(diào)用該函數(shù)進(jìn)行運(yùn)算

    • 打印結(jié)果

void menu()//封裝菜單
{printf("******************************\n");printf("****  1. add     2. sub   ****\n");printf("****  3. mul     4. div   ****\n");printf("****  0. exit             ****\n");printf("******************************\n");
}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;
}int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();//函數(shù)指針數(shù)組的方式解決一下//這里的函數(shù)指針數(shù)組,我們稱為轉(zhuǎn)移表// 為什么這里要加NULL,直接{add,sub,mul,div}不行嗎?如果真是可以的話,那我們觀察他的下標(biāo)  0   1   2   3此時(shí)此刻,如果我們選擇1.add,那么(*p[1])取出的地址是sub,這不對(duì)呀,如果我們?cè)谇懊婕右粋€(gè)NULL{ NULL,add,sub,mul,div }下標(biāo)為  0    1   2   3   4地址:*p[ 0 ] == 0,     *p[ 1 ] ==addint (*pfArr[])(int, int) = { NULL, Add, Sub, Mul, Div };//                           0     1    2    3    4printf("請(qǐng)選擇:");scanf("%d", &input);if (input == 0){printf("退出計(jì)算器\n");}else if (input >= 1 && input <= 4){										printf("請(qǐng)輸入兩個(gè)操作數(shù):");scanf("%d %d", &x, &y);ret = pfArr[input](x, y);           //(*p[input])==add/sub/mul/div函數(shù)名,也就是函數(shù)的地址//(p[input])也可以,*號(hào)有無,都相當(dāng)于函數(shù)名,也是函數(shù)地址// 也就是ret=(p[input])(x,y);printf("%d\n", ret);}else{printf("選擇錯(cuò)誤,重新選擇\n");}} while (input);return 0;
}

解釋:
當(dāng)input輸入1, pfArr[1]取得Add的地址,然后通過Add函數(shù)的地址,執(zhí)行指令。(當(dāng)然同理input輸入2,3,4也是同樣的步驟)。
如果要增加功能,那么可以int (*pfArr[])(int, int) = { NULL, Add, Sub, Mul, Div };增加相應(yīng)的功能,然后增加相應(yīng)功能的代碼塊!
比如,你想要增加位運(yùn)算(&, |, ^)的功能:

  1. 增加位運(yùn)算函數(shù):
int And(int x, int y) {return x & y;
}int Or(int x, int y) {return x | y;  
}int Xor(int x, int y) {return x ^ y;
}
  1. 修改菜單顯示:
void menu() {printf("******************************\n");printf("****  1. add     2. sub   ****\n"); printf("****  3. mul     4. div   ****\n");printf("****  5. &       6. |     ****\n");printf("****  7. ^              ****\n");  printf("****  0. exit             ****\n");printf("******************************\n");}
  1. 增加函數(shù)指針:
int (*pfArr[])(int, int) = {NULL, Add, Sub, Mul, Div, And, Or, Xor};
  1. 判斷函數(shù)選擇范圍:
if(input >= 1 && input <= 7) {ret = pfArr[input](x, y);}

這樣就增加了位運(yùn)算的功能選擇了。

如果還需要其他運(yùn)算,可以繼續(xù)增加對(duì)應(yīng)的函數(shù)和菜單顯示即可。

但是,思考———>
int (*p[5])(int x, int y) = { NULL,add,sub,mul,div };那函數(shù)的add,sub,mul,div這些地址是不是也和一維數(shù)組一樣,存儲(chǔ)在函數(shù)指針數(shù)組里面,他們的地址是否是連續(xù)的呢?
解釋:

函數(shù)地址在函數(shù)指針數(shù)組中的存儲(chǔ)方式與一維數(shù)組類似,但有一點(diǎn)不同:

  • 函數(shù)指針數(shù)組pfArr中,add、sub等函數(shù)地址的存儲(chǔ)是連續(xù)的,就像一維數(shù)組元素一樣,如下標(biāo)0,1,2,3,4這樣連續(xù)存儲(chǔ)后就可以訪問了。

  • 但是,函數(shù)本身的代碼可能不一定存儲(chǔ)在連續(xù)內(nèi)存地址中

更準(zhǔn)確地說:

  • 在函數(shù)指針數(shù)組pfArr中,add、sub等函數(shù)地址是以連續(xù)方式存儲(chǔ)的。

  • 而函數(shù)本身的代碼可能分散在不同代碼段(code section)中,地址不一定連續(xù)。

舉個(gè)例子:

假設(shè)add函數(shù)代碼在地址0x00001000,sub函數(shù)代碼在0x00002000,mul在0x00003000。

那么在函數(shù)指針數(shù)組pfArr中:

pfArr[1] 指向 add函數(shù)地址 0x00001000  
pfArr[2] 指向 sub函數(shù)地址 0x00002000
pfArr[3] 指向 mul函數(shù)地址 0x00003000

我們可以看到,pfArr[1]、pfArr[2]、pfArr[3]中的函數(shù)地址是以連續(xù)的方式存儲(chǔ)在數(shù)組中的。

但是函數(shù)本身的代碼地址0x00001000、0x00002000、0x00003000并不連續(xù)。

所以總結(jié)來說:

  • 函數(shù)指針數(shù)組pfArr中函數(shù)地址是連續(xù)存儲(chǔ)的
  • 但函數(shù)代碼本身不一定連續(xù)存儲(chǔ)在內(nèi)存中

在這里插入圖片描述

二、回調(diào)函數(shù)是什么?

C語言中的回調(diào)函數(shù)是指在函數(shù)調(diào)用的過程中,被另外一個(gè)函數(shù)作為參數(shù)傳遞并調(diào)用的函數(shù)。

回調(diào)函數(shù)的主要特征如下:

  1. 回調(diào)函數(shù)必須事先定義。

  2. 回調(diào)函數(shù)的地址作為參數(shù)傳遞給另一個(gè)函數(shù),這個(gè)函數(shù)稱為主函數(shù)。

  3. 主函數(shù)在適當(dāng)?shù)臅r(shí)候,通過調(diào)用回調(diào)函數(shù)的地址來調(diào)用回調(diào)函數(shù)。

一個(gè)典型的回調(diào)函數(shù)使用場(chǎng)景例子:

// 回調(diào)函數(shù)定義
void callback_func(int value) 
{printf("value is %d\n", value);
}// 主函數(shù)定義
void main_func(void (*callback)(int)) 
{int num = 10;// 調(diào)用回調(diào)函數(shù)callback(num); 
}int main() 
{// 注冊(cè)回調(diào)函數(shù)main_func(callback_func);return 0;
}

注:回調(diào)函數(shù)的特點(diǎn)是函數(shù)的調(diào)用關(guān)系由使用者在運(yùn)行時(shí)決定,而不是在編譯時(shí)就確定,這提供了更大的靈活性。

那可不可以使用回調(diào)函數(shù)實(shí)現(xiàn)計(jì)算器呢?

  1. 定義一個(gè)通用的計(jì)算函數(shù)calc,它接收一個(gè)函數(shù)指針作為參數(shù)。
  2. main函數(shù)中,根據(jù)用戶選擇直接調(diào)用calc函數(shù),并傳入相應(yīng)的運(yùn)算函數(shù)。
  3. 回調(diào)函數(shù)是Add、Sub、Mul、Div

在這里插入圖片描述

void menu()
{printf("******************************\n");printf("****  1. add     2. sub   ****\n");printf("****  3. mul     4. div   ****\n");printf("****  0. exit             ****\n");printf("******************************\n");
}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;
}//calc功能強(qiáng)大了void calc(int (*pf)(int,int))
{int x = 0;int y = 0;int ret = 0;printf("請(qǐng)輸入兩個(gè)操作數(shù):");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do{menu();printf("請(qǐng)選擇:");scanf("%d", &input);switch (input){case 1:calc(Add);//完成計(jì)算	break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出計(jì)算器\n");break;default:printf("選擇錯(cuò)誤,重新選擇\n");break;}} while (input);return 0;
}

在這里插入圖片描述

三、qsort函數(shù)細(xì)解

3.1 類比冒泡排序?

通過前面我們學(xué)過冒泡排序,qsort函數(shù)的排序讓我們類比一下:

void bubble_sort(int arr[], int sz)
{//趟數(shù)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]){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");
}int main()
{//將一組整數(shù)排序?yàn)樯?/span>int arr[10] = { 3,1,5,2,4,6,8,7,0,9 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);bubble_sort(arr, sz);print_arr(arr, sz);return 0;
}

在這里插入圖片描述

在這里插入圖片描述

3.2 qosrt函數(shù)超詳解

庫函數(shù)的學(xué)習(xí)和查看?具很多,?如:
C/C++官?的鏈接:https://zh.cppreference.com/w/c/header
cplusplus.com:https://legacy.cplusplus.com/reference/clibrary/
qsort函數(shù)是C標(biāo)準(zhǔn)庫中用于對(duì)數(shù)組進(jìn)行快速排序的函數(shù)。(注:qsort函數(shù)底層使用的排序算法就是快速排序。)

  • 函數(shù)原型:
void qsort(void* base,//base 指向了要排序的數(shù)組的第一個(gè)元素size_t num, //base指向的數(shù)組中的元素個(gè)數(shù)(待排序的數(shù)組的元素的個(gè)數(shù))size_t size,//base指向的數(shù)組中元素的大小(單位是字節(jié))int (*compar)(const void*p1, const void*p2)//函數(shù)指針 - 指針指向的函數(shù)是用來比較數(shù)組中的2個(gè)元素的);

.- [ ] 分析定義:

  • base指向要排序的數(shù)組首地址。

  • num表示數(shù)組元素個(gè)數(shù)。

  • size表示每個(gè)元素的大小,以字節(jié)為單位。

  • compar是比較函數(shù),它接收兩個(gè)void指針,返回值小于0表示第一個(gè)參數(shù)小于第二個(gè)參數(shù),等于0表示相等,大于0表示第一個(gè)參數(shù)大于第二個(gè)參數(shù)。

.- [ ] 特點(diǎn):

  • qsort使用快速排序算法,時(shí)間復(fù)雜度為O(nlogn)。

  • 調(diào)用qsort時(shí),需要提供一個(gè)比較函數(shù)compar來判斷兩個(gè)元素的大小關(guān)系。

  • 比較函數(shù)通過void指針間接訪問元素,避免與數(shù)據(jù)類型綁定,實(shí)現(xiàn)了最大程度的通用性。

  • qsort會(huì)在內(nèi)部調(diào)用比較函數(shù)多次對(duì)數(shù)組進(jìn)行排序,這就是回調(diào)機(jī)制的實(shí)現(xiàn)。

  • qsortinplace排序,不需要額外的空間。

在這里插入圖片描述

在這里插入圖片描述

3.2.1qsort函數(shù)排序整型數(shù)據(jù)

#include <stdio.h>
#include <stdlib.h>void print_arr(int arr[], int sz)
{//打印函數(shù)int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}int cmp_int(const void* p1, const void* p2)
{//好比冒泡排序return *(int*)p1 - *(int*)p2;
}//測(cè)試qsort排序整型數(shù)據(jù)的
int main()
{int arr[10] = { 4,2,5,3,1,6,7,8,0,9 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);//打印前qsort(arr, sz, sizeof(arr[0]), cmp_int);//arr->數(shù)組首元素地址//sz->數(shù)組元素個(gè)數(shù)也可以這樣寫sz==sizeof(arr)/sizeof(arr[0])//sizeof(arr[0])->數(shù)組元素大小,這里字節(jié)大小為4//cmp_int比較函數(shù)print_arr(arr, sz);//打印后
}

在這里插入圖片描述

3.2.2 使?qsort排序結(jié)構(gòu)數(shù)據(jù)

  1. 定義結(jié)構(gòu)體類型
struct Stu
{char name[20];//名字int age;
};
  1. 定義比較函數(shù)
  • 怎么比較2個(gè)結(jié)構(gòu)體數(shù)據(jù)? - 不能直接使用 > < ==比較
  • 可以按照名字比較
  • 可以按照年齡比較
//按照年齡比較
int cmp_stu_by_age(const void* p1, const void* p2)
{return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age;
}
  1. 聲明結(jié)構(gòu)體數(shù)組和元素個(gè)數(shù)
void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 38}, {"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}

代碼實(shí)現(xiàn):

# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>測(cè)試qsort函數(shù)排序結(jié)構(gòu)體數(shù)據(jù)
struct Stu
{char name[20];//名字int age;
};int cmp_stu_by_age(const void* p1, const void* p2)
{return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age;
}
void print(struct Stu arr[], int sz)
{for (int i = 0; i < sz; i++){printf("%s %d\n", arr[i].name, arr[i].age);}
}void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 38}, {"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);print(arr, sz);
}//	//兩個(gè)字符串不能使用> < ==
//	//而是使用庫函數(shù)strcmp - string compareint cmp_stu_by_name(const void* p1, const void* p2){return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);}void test3(){struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 38}, {"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);print(arr, sz);}int main()
{test3();//姓名排序//test2();//年齡排序return 0;
}

運(yùn)行test2()//年齡排序
在這里插入圖片描述

四、 qsort函數(shù)的模擬實(shí)現(xiàn)

4.1 模擬qsort整形數(shù)據(jù)

  1. 主函數(shù):

    • 定義int測(cè)試數(shù)據(jù)
    • 調(diào)用bubble排序
    • 打印結(jié)果驗(yàn)證
#include <stdio.h>
int main()
{int arr[] = { 3, 1, 5, 8, 4, 2, 9, 6, 7, 0 };int i = 0;  //元素個(gè)數(shù)                   //元素大小bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);//數(shù)組首元素地址arr                                    //比較函數(shù)for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}printf("\n");return 0;
}
  1. bubble函數(shù):

    • 接收基地址、元素個(gè)數(shù)、元素大小、比較函數(shù)作為參數(shù)
    • 實(shí)現(xiàn)冒泡排序核心算法
    • 通過傳遞的比較函數(shù)int_cmp來決定排序順序
    • 使用_swap函數(shù)交換元素
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))
{int i = 0;int j = 0;for (i = 0; i < count - 1; i++){for (j = 0; j < count - i - 1; j++){if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0){_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}
  1. int_cmp函數(shù):

    • 定義一個(gè)int類型的數(shù)據(jù)比較函數(shù)
    • 通過減法實(shí)現(xiàn)兩個(gè)int*指針所指向的數(shù)據(jù)比較
    • 返回值大于0表示p1大于p2(升序),小于0表示p1小于p2(降序)
    • 實(shí)現(xiàn)了冒泡排序中的比較規(guī)則
int int_cmp(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}
  1. _swap函數(shù):

    • 定義泛型數(shù)據(jù)交換函數(shù)
    • 通過循環(huán)交換每個(gè)字節(jié)實(shí)現(xiàn)數(shù)據(jù)交換
    • 使用char*來交換,實(shí)現(xiàn)數(shù)據(jù)類型無關(guān)
void _swap(void* p1, void* p2, int size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}
}

在這里插入圖片描述

總結(jié):
每個(gè)代碼塊實(shí)現(xiàn)的功能:

  • 主函數(shù): 測(cè)試驅(qū)動(dòng)開發(fā)
  • bubble: 實(shí)現(xiàn)冒泡排序算法
  • int_cmp: 提供比較規(guī)則
  • _swap: 實(shí)現(xiàn)泛型數(shù)據(jù)交換

在這里插入圖片描述

4.2 模擬qsort排序結(jié)構(gòu)數(shù)據(jù)

各個(gè)代碼塊分析如下:

  1. struct Stu定義結(jié)構(gòu)體類型,包含姓名和年齡字段。
# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>struct Stu
{char name[20];int age;
};
  1. Swap函數(shù)實(shí)現(xiàn)泛型數(shù)據(jù)交換,通過循環(huán)交換每個(gè)字節(jié)實(shí)現(xiàn)數(shù)據(jù)交換。
void Swap(char* buf1, char*buf2, size_t width)
{int i = 0;for (i = 0; i < width; i++) {char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
  1. bubble_sort2函數(shù)實(shí)現(xiàn)冒泡排序算法,和普通冒泡排序區(qū)別在于使用void*作為參數(shù),通過width實(shí)現(xiàn)對(duì)結(jié)構(gòu)體進(jìn)行排序。
void bubble_sort2(void* base, int sz, int 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){/*int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;*///交換Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}
  1. cmp_stu_by_age函數(shù)實(shí)現(xiàn)結(jié)構(gòu)體比較規(guī)則,根據(jù)年齡字段進(jìn)行比較。
int cmp_stu_by_age(const void* p1, const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}
  1. test4函數(shù)定義測(cè)試數(shù)據(jù),調(diào)用bubble_sort2進(jìn)行排序,打印結(jié)果驗(yàn)證。
void test4()
{struct Stu arr[] = { {"zhangsan", 18},{"lisi", 35},{"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort2(arr, sz, sizeof(arr[0]), cmp_stu_by_age);//打印arr數(shù)組的內(nèi)容int i = 0;for (i = 0; i < sz; i++){printf("%s %d\n", arr[i].name, arr[i].age);}
}
  1. main函數(shù)調(diào)用test4函數(shù)進(jìn)行測(cè)試。
int main()
{//模擬結(jié)構(gòu)體按年齡排序test3();return 0;
}

在這里插入圖片描述

小總結(jié)

  • struct Stu定義了需要排序的數(shù)據(jù)類型
  • Swap函數(shù)實(shí)現(xiàn)數(shù)據(jù)交換
  • bubble_sort2實(shí)現(xiàn)泛型冒泡排序
  • cmp_stu_by_age定義了結(jié)構(gòu)體比較規(guī)則
  • test4函數(shù)測(cè)試驅(qū)動(dòng)開發(fā)

總結(jié)

一、轉(zhuǎn)移表
利用函數(shù)指針數(shù)組實(shí)現(xiàn)轉(zhuǎn)移表是動(dòng)態(tài)規(guī)劃算法解決最優(yōu)子結(jié)構(gòu)問題時(shí)使用的一種技術(shù)。它記錄了子問題的解,避免重復(fù)計(jì)算。

二、回調(diào)函數(shù)是什么?
回調(diào)函數(shù)是指在函數(shù)調(diào)用后,被當(dāng)作參數(shù)傳遞給另一個(gè)函數(shù)的函數(shù)。調(diào)用方在需要時(shí),會(huì)調(diào)用被調(diào)用方內(nèi)部的這個(gè)函數(shù)。

三、qsort函數(shù)細(xì)解

3.1 類比冒泡排序?
qsort函數(shù)實(shí)現(xiàn)的也是冒泡排序算法。不同之處在于:

  • qsort是通用排序函數(shù),可以對(duì)任意數(shù)據(jù)類型進(jìn)行排序,而冒泡排序只能對(duì)數(shù)組進(jìn)行排序;

  • qsort通過回調(diào)函數(shù)來指定元素的比較方式,而冒泡排序直接比較元素值;

  • qsort內(nèi)部實(shí)現(xiàn)采用快速排序思想,而不是純粹的冒泡排序。

3.2 qsort函數(shù)超詳解

qsort函數(shù)原型:

void qsort(void *base, size_t num, size_t size, int (*compar)(const void*, const void*));
  • base:待排序數(shù)組首地址
  • num:數(shù)組元素個(gè)數(shù)
  • size:每個(gè)元素大小
  • compar:比較函數(shù)回調(diào),返回值小于0時(shí)交換元素

3.2.1 qsort排序整型數(shù)據(jù)

直接傳入整型比較函數(shù)如int cmp(const void*, const void*)

3.2.2 使用qsort排序結(jié)構(gòu)數(shù)據(jù)

定義結(jié)構(gòu)體比較函數(shù),通過強(qiáng)制類型轉(zhuǎn)換比較結(jié)構(gòu)體字段

四、qsort函數(shù)的模擬實(shí)現(xiàn)

4.1 模擬qsort整形排序
實(shí)現(xiàn)了一個(gè)簡(jiǎn)單快速排序函數(shù),可以對(duì)整型數(shù)組排序。

4.2 模擬qsort結(jié)構(gòu)體排序 同樣實(shí)現(xiàn)快速排序,但使用結(jié)構(gòu)體比較函數(shù)作為回調(diào)。

感謝你的收看,如果文章有錯(cuò)誤,可以指出,我不勝感激,讓我們一起學(xué)習(xí)交流,如果文章可以給你一個(gè)幫助,可以給博主點(diǎn)一個(gè)小小的贊😘
請(qǐng)?zhí)砑訄D片描述

http://www.risenshineclean.com/news/39040.html

相關(guān)文章:

  • 建材網(wǎng)站建設(shè) 南寧sem代運(yùn)營(yíng)托管公司
  • 中山制作企業(yè)網(wǎng)站廣州網(wǎng)站制作服務(wù)
  • 公司介紹網(wǎng)站怎么做只要做好關(guān)鍵詞優(yōu)化
  • 商城小程序定制公司搜索引擎優(yōu)化的重要性
  • 收錄網(wǎng)站是怎么做的網(wǎng)絡(luò)營(yíng)銷首先要
  • 織夢(mèng)網(wǎng)站在css中怎樣做導(dǎo)航關(guān)鍵詞優(yōu)化公司靠譜推薦
  • 2019做網(wǎng)站需要營(yíng)業(yè)執(zhí)照嗎2022最好的百度seo
  • 網(wǎng)頁源代碼搜索關(guān)鍵字如何seo推廣
  • php網(wǎng)站開發(fā)過程免費(fèi)下載b站視頻軟件
  • 云南昆明做網(wǎng)站西安競(jìng)價(jià)托管公司
  • 做網(wǎng)站需要什么基礎(chǔ)百度開發(fā)者平臺(tái)
  • 網(wǎng)站彈窗客服怎樣搭建自己的網(wǎng)站
  • 鹽城微網(wǎng)站建設(shè)廣州王牌seo
  • 開發(fā)一個(gè)網(wǎng)站的步驟推廣軟件賺錢的app
  • 廣州移動(dòng) 網(wǎng)站設(shè)計(jì)如何在各大平臺(tái)推廣
  • 天津建設(shè)工程信息網(wǎng)如何注冊(cè)網(wǎng)站優(yōu)化推廣招聘
  • 網(wǎng)站搭建搜外友鏈
  • 如何開網(wǎng)店無貨源不需要投資河北seo技術(shù)
  • 在設(shè)計(jì)賺錢的網(wǎng)站有哪些做網(wǎng)站需要多少錢
  • 廣東省農(nóng)業(yè)農(nóng)村廳官方網(wǎng)站成都網(wǎng)站快速開發(fā)
  • 建站源碼程序惠州seo外包服務(wù)
  • 自己的網(wǎng)站怎么開培訓(xùn)心得體會(huì)范文大全2000字
  • 外貿(mào)必看網(wǎng)站湖南百度seo
  • 做3d效果的網(wǎng)站亞馬遜關(guān)鍵詞排名提升
  • 松江品劃做網(wǎng)站云浮新增確診病例30例
  • 易企網(wǎng)站建設(shè)滁州網(wǎng)站seo
  • 系統(tǒng)優(yōu)化的方法知識(shí)點(diǎn)外貿(mào)建站優(yōu)化
  • 深圳網(wǎng)站建設(shè)加q479185700外貿(mào)網(wǎng)絡(luò)營(yíng)銷推廣
  • 保障性租賃住房管理平臺(tái)優(yōu)化大師班級(jí)優(yōu)化大師
  • 網(wǎng)站建設(shè)與開發(fā)論文谷歌seo是什么意思