C語言——深入理解指針(三)

C語言——深入理解指針(三)

1.回調函數是什么?

首先我們來回顧一下函數的直接調用

在這里插入圖片描述
回調函數就是通過函數指針調用的函數。我們將函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,被調用的函數就是回調函數。
在這里插入圖片描述

2.qsort函數

  • quick sort簡稱qsort,是C語言中提供的一個排序函數,是基于快速排序算法思想的一種排序算法。
  • 其優點有:現成的排序算法可直接使用;而且大部分情況下效率都是比冒泡排序高的;qsort函數可以排序任意類型的數據

function
在這里插入圖片描述

void qsort (void* base,//指針,指向了被排序數組的第一個元素size_t num, // 這里是base指向的被排序數組的元素個數size_t size,//這里是base指向的被排序數組的元素大小(長度),單位是字節int (*compar)(const void*,const void*)//函數指針,指針指向的函數是用來比較被排序數組中的兩個元素的);

在這里插入圖片描述
該函數排序默認為升序,希望為降序,只需將參數p1,p2順序反過來即可

3.qsort函數的使用

  • qsort函數對整型數組的排序:

我們在使用qsort函數排序時,常需要自己寫一個比較的邏輯,來比較整形數據的大小,如我們可以在這里寫一個int cmp_int(const void* p1,const void* p2)函數指針,而指針指向的函數是用來比較被排序數組中的兩個元素的,里面的p1,p2則分別指向一個整型變量,然后qsort函數根據返回的結果進行排序。整體整合下來如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//打印
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0;i < sz;i++){printf("%d ", arr[i]);}printf("\n");
}
//寫的是升序,若想改為降序,只需改變p1,p2的位置
int cmp_int(const void* p1,const void* p2)//const修飾函數參數,表示參數在函數體內不能被修改
{if (*(int*)p1 > *(int*)p2)//強制類型轉換為整型return 1;else if (*(int*)p1 < *(int*)p2)return -1;elsereturn 0;//根據qsort函數的排序邏輯,上面這一部分也可簡化為:return(*(int*)p1 - *(int*)p2);
}
void test()
{int arr[] = { 4,3,7,9,0,2,1,6 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);//打印排序前的數組//排序qsort(arr,sz,sizeof(arr[0]),cmp_int);print_arr(arr, sz);//打印排序后的數組
}
int main()
{test();return 0;
}

運行結果:
在這里插入圖片描述

  • qsort函數對結構體數據的排序:

1.按照年齡比較,只需比較整形數據的大小:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu
{char name[30];int age;
};
void print_stu(struct Stu arr[], int sz)
{int i = 0;for (i = 0;i < sz;i++){printf("%s:%d\n", arr[i].name, arr[i].age);}
}
int cmp_stu_by_age(const void* p1, const void* p2)
{return (*(struct Stu*)p1).age - (*(struct Stu*)p2).age;
}
void test()
{struct Stu arr[] = { {"Jack",28},{"Rose",25},{"liming",19} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);print_stu(arr, sz);
}
int main()
{test();return 0;
}

運行結果:
在這里插入圖片描述

2.按照名字比較,這里注意比較的是字符串的大小,注意這里不能使用> >= < <= == !=,需要使用strcmp()函數:

//按照名字比較
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu
{char name[30];int age;
};
//p1指向了一個結構體變量
//p2指向了一個結構體變量
void print_stu(struct Stu arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%s: %d\n", arr[i].name, arr[i].age);}printf("\n");
}
int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
void test()
{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_stu(arr, sz);
}
int main()
{test();return 0;
}

運行結果:
在這里插入圖片描述

4.qsort函數模擬實現

這里,依照qsort函數的邏輯,模擬實現一個底層邏輯為冒泡排序但可排序任意類型的數據的函數(即泛型編程)。

  • 設計bubble_sort函數,只是底層算法和qsort函數不一樣,但其參數可模擬qsort函數,第一個參數指向數組首元素的指針類比qsort我們記為base,同理第二個參數為size_t sz,第三個參數記為size_t width,第四個參數我們要寫的是函數指針,因為不知道將要比較什么類型的數據,所以用void修飾取出的元素,而函數的返回類型因為知曉是整型則為int即int (cmp)(const void p1, const void* p2)
  • 和冒泡排序的底層邏輯一樣,首先確定外層比較的趟數,然后內層決定趟內部比較的對數。外層只需要算出有幾個元素來確定需要幾趟,那么內層比較該如何比較?該如何確定一趟比較的對數呢?這里就不同于冒泡排序了
  • 這里需要將if(arr[j]>arr[j+1])修改,因為不知道類型,交換的時候并不僅僅是簡單的比較整型元素的大小,但是由于此時已經知道比較方法cmp(),所以只需調用一下cmp(),現在需要將arr[j]和arr[j+1]這兩個相鄰元素的地址傳到cmp()中一個給p1,一個給p2。現在只知道base指向數組首元素的地址,那該如何越過中間的元素獲取j和j+的地址呢?如下圖:
    在這里插入圖片描述
  • 下面一個問題就是如何交換這兩個元素。由于不能將它們整體交換,只知道這兩個元素的地址,所以可以將它們轉換為字節來進行交換,假設一個元素占四個字節,則可將第一個字節與第一個字節交換,以此類推從而實現元素的交換。寫一個Swap函數,其參數我們則需要傳兩個元素的地址(char*)base + j * width, (char*)base + (j + 1) * width和元素的寬度width,然后在交換函數中,使用一個for循環,讓其對應字節元素相交換就實現交換了。
    好,現在我們將其整合下來:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
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* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}
void Swap(char* buf1, char* buf2, size_t width)//交換 
{int i = 0;char tmp = 0;for (i = 0; i < width; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
void bubble_sort(void* base, size_t sz, size_t 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(cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}
void test()
{int arr[] = { 3,1,5,8,7,9,2,4,6,0 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}
int main()
{test();  return 0;
}

運行結果:
在這里插入圖片描述
上面已經可以完全排序整型數據了,現在我們來測試排序結構體數據,排序的思想一樣,繼續用bubble_sort()函數。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Stu
{char name[30];int age;
};
void print_stu(struct Stu arr[], int sz)
{int i = 0;for (i = 0;i < sz;i++){printf("%s:%d\n", arr[i].name, arr[i].age);}
}
int cmp_stu_by_age(const void* p1, const void* p2)
{return (*(struct Stu*)p1).age - (*(struct Stu*)p2).age;
}
void Swap(char* buf1, char* buf2, size_t width)//交換 
{int i = 0;char tmp = 0;for (i = 0; i < width; i++){tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
void bubble_sort(void* base, size_t sz, size_t 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(cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}
void test()
{struct Stu arr[] = { {"Jack",28},{"Rose",25},{"liming",19} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);//bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);print_stu(arr, sz);
}
int main()
{test();
}

按年齡排序的運行結果:
在這里插入圖片描述

到這里今天的內容就結束了
謝謝觀看!
這篇內容是我在qsort函數上的總結,如果你覺得有用,不妨點個贊收藏一下讓更多人看到,非常歡迎在評論區交流指正,一起進步~感謝讀到這里的每一位朋友!
“技術的路上,一個人走可能會很慢,但一群人同行就會更有力量!”

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

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

相關文章

kettle 8.2 ETL項目【四、加載數據】

一、dim_store表結構,數據來源于業務表,且隨時間會有增加,屬于緩慢變化維(SCD)類型二 轉換步驟如下 詳細步驟如下

【測試報告】SoundWave(Java+Selenium+Jmeter自動化測試)

一、項目背景 隨著數字音樂內容的爆炸式增長&#xff0c;用戶對于便捷、高效的音樂管理與播放需求日益增強。傳統的本地音樂管理方式已無法滿足多設備同步、在線分享與個性化推薦等現代需求。為此&#xff0c;我們設計并開發了一款基于Spring Boot框架的SoundWave&#xff0c;旨…

C++ 類和對象詳解(1)

類和對象是 C 面向對象編程的核心概念&#xff0c;它們為代碼提供了更好的封裝性、可讀性和可維護性。本文將從類的定義開始&#xff0c;逐步講解訪問限定符、類域、實例化、對象大小計算、this 指針等關鍵知識&#xff0c;并對比 C 語言與 C 在實現數據結構時的差異&#xff0…

奈飛工廠:算法優化實戰

推薦系統的算法邏輯與優化技巧在流媒體行業的 “用戶注意力爭奪戰” 中&#xff0c;推薦系統是決定成敗的核心武器。對于擁有2.3 億全球付費用戶的奈飛&#xff08;Netflix&#xff09;而言&#xff0c;其推薦系統每天處理數十億次用戶交互&#xff0c;最終實現了一個驚人數據&…

【人工智能99問】BERT的訓練過程和推理過程是怎么樣的?(24/99)

文章目錄BERT的訓練過程與推理過程一、預訓練過程&#xff1a;學習通用語言表示1. 數據準備2. MLM任務訓練&#xff08;核心&#xff09;3. NSP任務訓練4. 預訓練優化二、微調過程&#xff1a;適配下游任務1. 任務定義與數據2. 輸入處理3. 模型結構調整4. 微調訓練三、推理過程…

[TryHackMe]Challenges---Game Zone游戲區

這個房間將涵蓋 SQLi&#xff08;手動利用此漏洞和通過 SQLMap&#xff09;&#xff0c;破解用戶的哈希密碼&#xff0c;使用 SSH 隧道揭示隱藏服務&#xff0c;以及使用 metasploit payload 獲取 root 權限。 1.通過SQL注入獲得訪問權限 手工注入 輸入用戶名 嘗試使用SQL注入…

北京JAVA基礎面試30天打卡09

1.MySQL存儲引擎及區別特性MyISAMMemoryInnoDBB 樹索引? Yes? Yes? Yes備份 / 按時間點恢復? Yes? Yes? Yes集群數據庫支持? No? No? No聚簇索引? No? No? Yes壓縮數據? Yes? No? Yes數據緩存? NoN/A? Yes加密數據? Yes? Yes? Yes外鍵支持? No? No? Yes…

AI時代的SD-WAN異地組網如何落地?

在全球化運營與數字化轉型浪潮下&#xff0c;企業分支機構、數據中心與云服務的跨地域互聯需求激增。傳統專線因成本高昂、部署緩慢、靈活性差等問題日益凸顯不足。SD-WAN以其智能化調度、顯著降本、敏捷部署和云網融合的核心優勢&#xff0c;成為實現高效、可靠、安全異地組網…

css中的color-mix()函數

color-mix() 是 CSS 顏色模塊&#xff08;CSS Color Module Level 5&#xff09;中引入的一個強大的顏色混合函數&#xff0c;用于在指定的顏色空間中混合兩種或多種顏色&#xff0c;生成新的顏色值。它解決了傳統顏色混合&#xff08;如通過透明度疊加&#xff09;在視覺一致性…

Github desktop介紹(GitHub官方推出的一款圖形化桌面工具,旨在簡化Git和GitHub的使用流程)

文章目錄**1. 簡化 Git 操作****2. 代碼版本控制****3. 團隊協作****4. 代碼托管與共享****5. 集成與擴展****6. 跨平臺支持****7. 適合的使用場景****總結**GitHub Desktop 是 GitHub 官方推出的一款圖形化桌面工具&#xff0c;旨在簡化 Git 和 GitHub 的使用流程&#xff0c;…

整數規劃-分支定界

內容來自:b站數學建模老哥 如:3.4,先找小于3的,再找大于4的 逐個

JetPack系列教程(六):Paging——讓分頁加載不再“禿”然

前言 在Android開發的世界里&#xff0c;分頁加載就像是一場永無止境的馬拉松&#xff0c;每次滾動到底部&#xff0c;都仿佛在提醒你&#xff1a;“嘿&#xff0c;朋友&#xff0c;還有更多數據等著你呢&#xff01;”但別擔心&#xff0c;Google大佬們早就看透了我們的煩惱&a…

扎實基礎!深入理解Spring框架,解鎖Java開發新境界

大家好&#xff0c;今天想和大家聊聊Java開發路上繞不開的一個重要基石——Spring框架。很多朋友在接觸SpringBoot、SpringCloud這些現代化開發工具時&#xff0c;常常會感到吃力。究其原因&#xff0c;往往是對其底層的Spring核心機制理解不夠透徹。Spring是構建這些高效框架的…

Heterophily-aware Representation Learning on Heterogeneous Graphs

Heterophily-Aware Representation Learning on Heterogeneous Graphs (TPAMI 2025) 計算機科學 1區 I:18.6 top期刊 ?? 摘要 現實世界中的圖結構通常非常復雜,不僅具有全局結構上的異質性,還表現出局部鄰域內的強異質相似性(heterophily)。雖然越來越多的研究揭示了圖…

計算機視覺(7)-純視覺方案實現端到端軌跡規劃(思路梳理)

基于純視覺方案實現端到端軌跡規劃&#xff0c;需融合開源模型、自有數據及系統工程優化。以下提供一套從模型選型到部署落地的完整方案&#xff0c;結合前沿開源技術與工業實踐&#xff1a; 一、開源模型選型與組合策略 1. 感知-預測一體化模型 ViP3D&#xff08;清華&#…

Nginx 屏蔽服務器名稱與版本信息(源碼級修改)

Nginx 屏蔽服務器名稱與版本信息&#xff08;源碼級修改&#xff09; 一、背景與目的 在生產環境部署 Nginx 時&#xff0c;默認配置會在 Server 響應頭中暴露服務類型&#xff08;如 nginx&#xff09;和版本號&#xff08;如 nginx/1.25.4&#xff09;。這些信息可能被攻擊者…

從鋼板內部應力視角,重新認識護欄板矯平機

一、為什么鋼板會“自帶波浪”&#xff1f; 鋼卷在熱軋后冷卻、卷取、長途運輸、多次吊運時&#xff0c;不同部位受到的溫度、張力、碰撞并不一致&#xff0c;內部會產生不均勻的殘余應力。應力大的區域想“伸長”&#xff0c;應力小的區域想“縮短”&#xff0c;宏觀上就表現為…

C++中的`auto`與`std::any`:功能、區別與選擇建議

引言 在C編程中&#xff0c;auto和std::any是兩個功能強大但用途不同的工具。理解它們的區別和適用場景對于編寫高效、可維護的代碼至關重要。本文將詳細介紹auto和std::any的基本概念、使用方法、適用場景以及它們之間的區別&#xff0c;并提供選擇建議&#xff0c;幫助開發者…

【Linux】進程(Process)

一、什么是進程二、進程的創建三、進程的狀態四、僵尸進程五、孤兒進程六、進程的優先級 以及 并發/并行七、進程的切換一、什么是進程&#xff1f;什么是進程呢(一)?官方話來說&#xff1a;進程是一個執行實例、正在執行的程序、是系統資源分配的基本單位按課本官方話可能有一…

銷售管理系統哪個好?14款軟件深度對比

本文將深入對比14款銷售管理系統&#xff1a;1.紛享銷客&#xff1b; 2.Zoho CRM&#xff1b; 3.神州云動 CRM&#xff1b; 4.勵銷云 CRM&#xff1b; 5.Microsoft Dynamics?365 CRM&#xff1b; 6.悟空 CRM&#xff1b; 7.泛微 CRM&#xff1b; 8.HubSpot CRM&#xff1b; 9.…