回調函數中 qsort 函數的使用

目錄

一.冒泡排序

二.指針類型 void*

三. qsort

1.簡介

?2.研究函數參數?

3.怎么用?

(1)排數組,升序

(2)排序結構體

四.用冒泡排序思想,模擬實現 qsort (可排序任意類型數據)

1.函數參數設計

2.在 if (cmp( )>0) 怎么傳參?

3.怎么交換?

五.整體代碼


一.冒泡排序

對數組進行排序,升序。思想:兩兩相鄰元素比較

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 - 1 - i; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}int main()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);//打印 0 1 2 3 4 5 6 7 8 9int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

10個元素9趟,sz 個元素 sz-1 趟。

第一趟10個元素比較9次,第二趟9個元素比較8次 ......

缺點:只能排序整數。因為他的參數已經寫死了,只能是整形數組。想排序字符數組、浮點型、結構體......做不到。他的功能非常有限。

二.指針類型 void*

先跳過,看完 三.2.后,回來看。

為什么 qsort 的參數寫的 void*(無具體類型的指針) ?

因為:函數設計者,不知道未來你會比較什么類型的數據。

int a = 10;
float f = 5.5f;
int* p = &a;//編譯器沒報錯
p = &f;//編譯器報錯:從“float *”到“int *” 的類型不兼容void* pp = &f;//不報錯
pp = &a;//不報錯

可以把 pp 理解為通用的指針。(垃圾桶)誰的地址往進放。

缺點:放進去,不能直接用

float f = 5.5f;
void* pp = &f;
printf("%f", *pp);

void* 類型的指針不能直接解引用。它沒有具體類型,自己都不知道自己是啥類型

pp++; 也不行。無具體類型,它也不知道它 + 一步,+ 多遠

當你都不知道未來別人給你傳誰的地址時,用 void* 類型的指針接收

怎么用?指針類型可以轉換

接著看 三.3.

三. qsort

qsort 內部的細節大家先不要關心,回頭小編帶大家模擬實現

1.簡介

是庫函數里提供的一個排序函數,底層用的是快速排序。頭文件 #include <stdlib.h>

?2.研究函數參數?

一共4個參數:1.待排序數組的起始地址。2.元素個數。3.一個元素的大小(字節)。

4.(函數指針)。int (*compar)(const void* e1, const void* e2)

先分析,對上面的 bubble_sort 函數部分進行改造。希望函數可以排整型、浮點型、結構體......(任意類型數據),但還是用冒泡排序的思想,趟數和一趟比較的對數都不用變。

11-15行要變,因為:兩個整型可以直接用 > 比較。但兩個結構體元素不能用 > 比。

不同數據的比較方法不一樣,交換方式不一樣

想讓他排任意類型的數據,把比較兩個元素的方法抽離出來。

比如:程序員A 想排序兩個整型,自己提供兩個整型的比較方法;想排序結構體,自己提供兩個結構體的比較方法。

再把比較的方法傳進來,你按照我的方法比較這兩個元素就可以。

其實,qsort 就是這么實現的。自己寫個比較兩個東西函數,函數的地址傳給 qsort ,恰好用(*compar)這個函數指針接收。然后 qsort 里面就按照這個函數指針指向的函數來比較。注意:&函數名? ? ? 函數名? ? ? ?都是地址

寫的函數就被稱為回調函數

e1 e2 分別是要比較的兩個元素的地址

3.怎么用?

(1)排數組,升序

#include <stdlib.h>
cmp_int()
{ }int main()
{int arr[] = { 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);// 這里是函數cmp_int 的地址int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

要把 cmp_int 的地址傳給 qsort 所需要的(*compar) 函數指針。

cmp_int 的參數、返回類型要與 qsort 的第四個參數的函數指針的參數、返回類型 保持一致

#include <stdlib.h>//實現一個比較整形的函數
int cmp_int(const void* e1, const void* e2)
{ //(int*)e1  把e1當成整型指針了return *(int*)e1 - *(int*)e2;
}int main()
{int arr[] = { 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);// 這里是函數cmp_int 的地址int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

cmp_int 實現了比較 e1 e2 指向的兩個整型。把 cmp_int 的地址傳給 qsort

qsort 就知道了,說:你讓我 比較 arr 里 sz 個元素,每個元素大小為 sizeof(arr[0]),數組arr里兩個元素的比較方法是 cmp_int

想降序:return *(int*)e2 - *(int*)e1;

(2)排序結構體

1.按照學生年齡排序

#include <stdlib.h>struct Stu
{char name[20];int age;
};int cmp_stu_by_age(const void* e1, const void* e2)
{// (struct Stu*)e2 - e2強制轉化為結構體類型指針return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}int main()
{struct Stu s[3] = { {"zhangsan",20}, {"lisi", 50}, {"wangwu", 33} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);return 0;
}

2.按照學生名字排序

#include <stdlib.h>
#include <string.h>
struct Stu
{char name[20];int age;
};int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}int main()
{struct Stu s[3] = { {"zhangsan",20}, {"lisi", 50}, {"wangwu", 33} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);return 0;
}

四.用冒泡排序思想,模擬實現 qsort (可排序任意類型數據)

1.函數參數設計

參數1:為了能夠接受任意類型地址,用 void*? 。? 參數2:元素個數

為什么 qsort 要傳元素大小?

void* 無具體類型,只知道待排序數組元素個數,不知道一個元素占幾個字節

參數3:寬度? ? 參數4:提出比較方法

類型最好給 size_t ,個數、寬度永遠是正數

//實現一個比較整型的函數
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}void bubble_sort(void* base, size_t sz, size_t 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() > 0){//交換}//if (arr[j] > arr[j + 1])//{//	int tmp = arr[j];//	arr[j] = arr[j + 1];//	arr[j + 1] = tmp;//}}}
}void test3()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(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;
}

2.在 if (cmp( )>0) 怎么傳參?

相鄰兩元素比較,傳過來要是兩元素的地址。比如,傳9 8地址。怎么算呢?

現在不知道里面放的什么類型的元素,沒法直接用下標來搞。只能通過偏移量來算。

8的地址是 base + 幾。但 base 是 void* ,不能直接加。

強制類型轉換。轉換成什么類型?

int ? 不行,寫死了。

怎么辦?9的寬度是 width,從9偏移 width 個字節來到8。怎么來到8呢?

寬度 width 的單位是字節,如果把它轉換成 char* 的指針,char* 的指針+width=跳過width個字節

if(cmp((char*)base, (char*)base + width) > 0) //只是第一對元素if(cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{//交換
}

if 里面是分別對應下標為 j? ?j+1 元素的地址,分別傳給 e1? ?e2 指針

cmp是比較函數,e1? ?e2? 是它比較的兩個元素

我們已經把待比較的兩個元素的地址傳給 e1? ?e2 。cmp 就通過這樣一個指針找到它所指向的比較函數。如果比較函數返回值 >0 就交換

3.怎么交換?

?給一個Swap 函數,把(char*)base + j * width , (char*)base + (j + 1) * width) 兩個指針指向的元素交換。

交換這兩個元素的寬度是多大呢?注意:我不知道我交換的兩個元素多大。把 width 傳過去

比較的時候都不知道比較誰,交換時也肯定不知道交換的是什么類型的數據,所以通過類型是沒辦法

if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
{//交換Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
}

但是注意,當我要交換兩個結構體的時候。我們沒辦法創建出10字節的臨時空間,但是可以創建一個字節的空間。

交換每一對字節,這兩個元素整體也就交換了

//已經強制轉換為char*,這里也不端著了,就寫成char* 的指針
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++;}
}

五.整體代碼

#include <stdio.h>
#include <string.h>struct Stu
{char name[20];int age;
};int cmp_stu_by_age(const void* e1, const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}//實現一個比較整型的函數
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}//已經強制轉換為char*,這里也不端著了,就寫成char* 的指針
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 bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{//趟數size_t i = 0;for (i = 0; i < sz - 1; i++){//一趟冒泡排序的過程size_t 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);}}}
}//使用我們自己寫的bubble_sort函數排序整型數組
void test3()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}//使用我們自己寫的bubble_sort函數排序結構體數組
void test4()
{struct Stu s[3] = { {"zhangsan",20}, {"lisi", 50}, {"wangwu", 33} };int sz = sizeof(s) / sizeof(s[0]);bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}int main()
{test3();test4();return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/898452.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/898452.shtml
英文地址,請注明出處:http://en.pswp.cn/news/898452.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電機控制常見面試問題(十四)

文章目錄 一.電機信噪比二.電機零點偏移校正和極對數自適應1.零點偏移量檢測?2. 極對數識別三.交流電機電流紋波怎么產生的1.電源相關因素2.電機本體特性3.?PWM逆變器諧波4.負載與環境干擾5.診斷流程建議 四.談談對諧波的理解1.諧波定義2.次諧波產生源3.次諧波的檢測與分析4.…

axios和fetch的對比

axios 和 fetch 是用于發起 HTTP 請求的兩種常見工具&#xff0c;它們的主要區別如下&#xff1a; 1. 瀏覽器兼容性 axios&#xff1a;基于 XMLHttpRequest&#xff0c;兼容性較好&#xff0c;支持較舊的瀏覽器&#xff08;如 IE11&#xff09;。fetch&#xff1a;現代瀏覽器…

Java Timer定時任務源碼分析

前言 Java 提供的java.util.Timer類可以用來執行延時任務&#xff0c;任務可以只執行一次&#xff0c;也可以周期性的按照固定的速率或延時來執行。 實現一個延時任務調度器&#xff0c;核心有兩點&#xff1a; 如何存儲延時任務如何調度執行延時任務 源碼分析 TimerTask …

【安全運營】用戶與實體行為分析(UEBA)淺析

目錄 用戶與實體行為分析&#xff08;UEBA&#xff09;簡介一、UEBA的核心概念1. 行為基線建立2. 異常檢測3. 風險評分4. 上下文關聯 二、UEBA的應用場景1. 內部威脅檢測2. 外部威脅應對3. 合規性和審計支持 三、UEBA的技術實現1. 大數據技術2. 機器學習算法3. 可視化工具 四、…

系統思考—啤酒游戲經營決策沙盤模擬

再次感謝文華學院的邀請&#xff0c;為經緯集團管理層帶來 《啤酒游戲經營決策沙盤》&#xff01; 很多朋友問&#xff1a;“最近是不是啤酒游戲上的少了&#xff1f;” 其實&#xff0c;真正的關鍵不是游戲本身&#xff0c;而是——如何讓大家真正看見復雜系統中的隱性結構。 …

排序算法實現:插入排序與希爾排序

目錄 一、引言 二、代碼整體結構 三、宏定義與頭文件 四、插入排序函數&#xff08;Insertsort&#xff09; 函數作用 代碼要點分析 五、希爾排序函數&#xff08;ShellSort&#xff09; 函數作用 代碼要點分析 六、打印數組函數&#xff08;PrintSort&#x…

redis的key是如何找到對應存儲的數據原理

在 Redis 中,Key 是數據的唯一標識符,而 Value 是與 Key 關聯的實際數據。Redis 通過高效的鍵值對存儲機制,能夠快速定位和訪問數據。以下是 Redis 如何通過 Key 找到對應存儲數據的詳細解析: 1. Redis 的數據存儲結構 Redis 是一個基于內存的鍵值存儲系統,其核心數據結構…

github上傳本地文件到遠程倉庫(空倉庫/已有文件的倉庫)

今天搞自己本地訓練的代碼到倉庫留個檔&#xff0c;結果遇到了好多問題&#xff0c;到騰了半天才搞明白整個過程&#xff0c;留在這里記錄一下。 遠程空倉庫 主要根據官方教程&#xff1a;Adding locally hosted code to GitHub - GitHub Docs #1. cd到你需要上傳的文件夾&a…

Redis數據類型詳解

Redis數據類型詳解 Redis 共有 5 種基本數據類型&#xff1a;String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、Set&#xff08;集合&#xff09;、Hash&#xff08;散列&#xff09;、Zset&#xff08;有序集合&#xff09;。 除了 5 種基本的數據…

【第13章】億級電商平臺訂單系統-高性能之異步架構設計

1-1 本章導學 課程導學 學習目標:掌握大型系統架構設計難點之高性能異步架構設計項目落地:訂單系統高性能異步架構設計(年交易200億B2B電商平臺)本章主要內容 1. 為何需要異步消息架構 分析同步架構的性能瓶頸異步架構對系統解耦與性能提升的核心價值2. 確定異步消息技術…

2025-03-20 學習記錄--C/C++-C 庫函數 - toupper()、tolower()、 isspace()

合抱之木&#xff0c;生于毫末&#xff1b;九層之臺&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、C 庫函數 - toupper() ?? C 標準庫 - <ctype.h> C 標準庫的 ctype.h 頭文件提供了一些函數&#xff0c;可用于測試和…

LoRaWAN技術解析

LoRaWAN&#xff08;Long Range Wide Area Network&#xff09;是一種基于 LoRa&#xff08;Long Range&#xff09;技術的低功耗廣域網絡協議&#xff0c;專為物聯網&#xff08;IoT&#xff09;設備的無線通信而設計。它是一種開放的、標準化的通信協議&#xff0c;支持大規模…

織夢DedeCMS如何獲得在列表和文章頁獲得頂級或上級欄目名稱

獲得頂級或二級欄目的名稱&#xff0c;都需要修改php文件&#xff0c;修改的文件【/include/common.func.php】將代碼插入到這個文件的最下面即可&#xff1b; 一、獲得當前文章或欄目的【頂級欄目】名稱 1、插入頂級欄目代段 //獲取頂級欄目名 function GetTopTypename($id…

虛幻基礎:ue自定義類

文章目錄 Gameplay Tag&#xff1a;ue標簽類創建&#xff1a;其他-數據表格-gameplaytag安裝&#xff1a;項目設置&#xff1a;gamePlayTag&#xff1a;gamePlay標簽列表使用&#xff1a;變量類型&#xff1a;gamePlayTag primary data asset&#xff1a;ue數據類&#xff1a;通…

易語言模擬真人鼠標軌跡算法

一.簡介 鼠標軌跡算法是一種模擬人類鼠標操作的程序&#xff0c;它能夠模擬出自然而真實的鼠標移動路徑。 鼠標軌跡算法的底層實現采用C/C語言&#xff0c;原因在于C/C提供了高性能的執行能力和直接訪問操作系統底層資源的能力。 鼠標軌跡算法具有以下優勢&#xff1a; 模擬…

Matplotlib 柱形圖

Matplotlib 柱形圖 引言 在數據可視化領域&#xff0c;柱形圖是一種非常常見且強大的圖表類型。它能夠幫助我們直觀地比較不同類別或組之間的數據大小。Matplotlib&#xff0c;作為Python中最受歡迎的數據可視化庫之一&#xff0c;提供了豐富的繪圖功能&#xff0c;其中包括創…

sparksql的Transformation與 Action操作

Transformation操作 與RDD類似的操作 map、filter、flatMap、mapPartitions、sample、 randomSplit、 limit、 distinct、dropDuplicates、describe&#xff0c;而以上這些都是企業中比較常用的&#xff0c;這里在一個文件中統一論述 val df1 spark.read.json("src/m…

微軟Data Formulator:用AI重塑數據可視化的未來

在數據驅動的時代,如何快速將復雜數據轉化為直觀的圖表是每個分析師面臨的挑戰。微軟研究院推出的開源工具 Data Formulator,通過結合AI與交互式界面,重新定義了數據可視化的工作流。本文將深入解析這一工具的核心功能、安裝方法及使用技巧,助你輕松駕馭數據之美。 一、Dat…

20分鐘上手DeepSeek開發:SpringBoot + Vue2快速構建AI對話系統

20分鐘上手DeepSeek開發&#xff1a;SpringBoot Vue2快速構建AI對話系統 前言 在生成式AI技術蓬勃發展的今天&#xff0c;大語言模型已成為企業智能化轉型和個人效率提升的核心驅動力。作為國產大模型的優秀代表&#xff0c;DeepSeek憑借其卓越的中文語義理解能力和開發者友…

神經網絡中層與層之間的關聯

目錄 1. 層與層之間的核心關聯&#xff1a;數據流動與參數傳遞 1.1 數據流動&#xff08;Forward Propagation&#xff09; 1.2 參數傳遞&#xff08;Backward Propagation&#xff09; 2. 常見層與層之間的關聯模式 2.1 典型全連接網絡&#xff08;如手寫數字分類&#xf…