【c語言】深入理解指針3——回調函數

一、回調函數

回調函數:通過函數指針調用的函數.

當把一個函數的地址傳遞給另一個函數,通過該地址去調用其指向的函數,那么這個被調用的函數就是回調函數.

示例:
在【深入理解指針2】中結尾寫了用函數指針實現計算器的功能,如下:

void menu()
{printf("1.add   2. sub \n");printf("3.mul   4. div \n");printf("0.exit \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;
}
void calc(int (*pf)(int, int))
{int a, b;printf("請輸入兩個參數:");scanf_s("%d %d", &a, &b);int ret = pf(a, b);printf("%d \n", ret);
}
int main()
{int a = 0;int b = 0;int input = 0;int ret = 0;do {menu();printf("請選擇:");scanf_s("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);case 4:calc(Div);break;case 0:printf("退出\n");break;default:printf("請重新輸入:");break;}} while (input);return 0;
}

這里加入想實現加法函數的功能,并沒有直接在主函數中直接調用加法函數Add,而是通過另一個函數calc中函數指針變量來接受Add函數的地址,然后通過函數指針變量再去調用Add函數,因此,這里用函數指針去調用的那個函數就是回調函數,即Add、Sub、Mul、Div是回調函數.

二、qsort函數使用

官網對qsort函數的說明:

void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));      

qsort函數有四個參數:

  1. void* base : base是一個指針,指向待排序數組的首元素地址
  2. size_t num : num是指待排序數組中元素的個數
  3. size_t size :size是指待排序數組中每個元素的大小
  4. compar : 函數指針,指向兩個元素的比較函數,此函數需要自己實現

在這里插入圖片描述
假設兩元素為p1,p2,若p1<p2,則返回小于0的值;若p1=p2,則返回等于0的值;若p1>p2,則返回大于0的值.

排序整型數據

#include <stdlib.h>
#include<stdio.h>
int cmp(void* e1, void* e2)//比較函數
{return *(int*)e1 - *(int*)e2;//void* 不能解引用,要強制類型轉換//此方式默認升序排列,若想實現降序,可用e2-e1,將邏輯改反
}
void test()
{int arr[] = { 9,8,7,6,5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]),cmp );int i = 0;for (i = 0;i < sz;i++){printf("%d ", arr[i]);}}
int main()
{test();return 0;
}

排序結構體數據

#include <string.h>
struct stu
{char name[20];int age;
};
int cmp_name(const void* e1,const void* e2)
{return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
void test()
{struct stu arr[3] = { {"zhangsan",18},{"lishi",25},{"wangwu",22} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_name);}
int main()
{test();return 0;
}

其中,strcmp函數比較時,比較的是對應字符的ASCII碼值,并不比較字符串長度,比如:

  1. “abc"和"abd” : a和a的ASCII碼值相等,比較b和b,也相等,再比較c和d,d的ASCII碼值大,則"abc"小于"abd"
  2. “aef"和"abcde” : a的ASCII碼值相同,e的ASCII碼值大于b,則不再進行比較,認為"aef"大于"abcde".

在這里插入圖片描述

可以看出 ’ l ’ < ’ w ’ < ’ z ',因此 " lishi " 排第一

三、qsort函數模擬實現

用冒泡排序實現qsort函數

冒泡排序算法:

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 - i - 1;j++)//交換次數{if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = arr[j];}}}
}

我們想排序的數據類型如果不是整型,是結構體類型、字符類型等,在bubble_sort函數參數部分就不可以用整型數組來接收,因此,參考qsort函數的定義:

void bubble_sort(void* base, size_t sz, size_t width, int(*cmp)(const void* p1, const void* p2));
  1. 冒泡排序首先要知道要比較的數據的首元素地址,但是不知道具體是什么類型,因此用void* base來接收
  2. 需要知道數據的個數,類型為無符號的整型,用sz接收
  3. 在我們拿到首元素地址之后,向后找元素進行比較,需要知道每個元素的內存大小,不同類型元素的大小不同,但都是整型,因此用width接收,與qsort函數的size相似
  4. 最后需要傳入對元素進行比較的函數的地址,類型為函數指針,因為并不知道要比較的元素類型,因此用void* 類型的指針進行接收,進行比較時根據不同的類型進行強制類型轉換,然后用返回值來判斷他們的大小關系,因此比較函數返回類型為 int

然后分析冒泡排序函數內部:其中排序的趟數和元素交換次數不需要變
在這里插入圖片描述

  1. 在元素進行比較時,如果元素類型不是整型,如結構體類型、字符類型等就不可用大于小于這種方式來比較,因此應該用函數指針cmp去調用 用來比較的函數進行元素比大小
  2. 在元素交換部分,交換的元素類型不能確定,肯定不能使用 int 類型,因此將其封裝成函數,在其內部進行操作,具體思路如下:

在這里插入圖片描述

在這里插入圖片描述

拿到元素地址的目的是為了進行元素的交換. 首先,base是無符號指針,應該先對其進行強制類型轉換成char*類型,那么base+4就拿到下一個元素的地址,base+8拿到第三個元素的地址,以此類推…
每個元素的內存大小為width,那么第一個元素地址為 (char*) base + j * width , 第二個元素地址為 (char*) base + (j+1) * width ,拿到兩個元素地址后進行交換,交換完成后 j++,這樣繼續取得第二個和第三個元素的地址,繼續進行操作

有人會問,為什么base要強轉成char*類型,轉換成int*不行嗎?

我們不知道要排序的元素類型是什么,可能是整型,可能是結構體類型,可能是字符類型等等,每個元素的大小可能是1、4、7、9等,那么在解引用,進行交換元素的時候,由于整型一次只能交換4個字節的內存,在交換7、9字節的內存時候就不行了,char*類型進行解引用是char類型,每次只交換一個字節,這樣無論元素是什么類型都可以交換完成

因此,由于排序元素的類型不確定性,選擇一個字節一個字節進行交換的方式完成排序,進而選擇(char*)base
具體實現代碼如下:

//比較函數由自己編寫,只有使用者知道要排序什么類型的元素
int cmp_int(const void* e1, const void* e2)
{return strcmp((char*)e1 , (char*)e2);
}
void Swap(char* buf1, char* buf2,size_t width)
{char 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 - i - 1;j++){if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}}}
}int main()
{char arr[] = "aksjdu";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("%c ", arr[i]);}return 0;
}

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

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

相關文章

HTTP 核心概念

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;…

VidBot:從野外 2D 人體視頻中學習可泛化的 3D 動作,實現零樣本機器人操控

25年3月來自慕尼黑工大、瑞士 ETH 和微軟的論文“VidBot: Learning Generalizable 3D Actions from In-the-Wild 2D Human Videos for Zero-Shot Robotic Manipulation”。 未來的機器人被設想為能夠執行各種家務的多功能系統。最大的問題仍然是&#xff0c;如何在盡量減少機器…

Linux 日常運維命令大全

Linux 作為一種開源操作系統&#xff0c;在服務器運維中扮演著重要角色。掌握常用的 Linux 命令對于運維人員而言至關重要。本文將整理一份 Linux 服務器運維常用命令大全&#xff0c;幫助你在日常工作中提高效率和準確性。 1. 基礎命令 基礎命令是Linux操作的起點&#xff0…

編程規范之枚舉

編程規范之枚舉 1.1 初始化枚舉項 枚舉平時用的也沒有很頻繁&#xff0c;今天看代碼規范提到枚舉類型初始化枚舉項。并對初始化枚舉項進行了歸納。包括下面三個 不進行顯示初始化&#xff0c;交由編譯器完成。 對第一個枚舉項的顯式初始化&#xff0c;這樣可以強制整數值的…

《軟件設計師》復習筆記(12.1)——范圍管理、進度管理

目錄 一、范圍管理 1. 核心概念 2. 范圍管理過程 WBS&#xff08;工作分解結構&#xff09;示例 真題示例&#xff1a; 二、進度管理 1. 核心過程 2. 關鍵工具與技術 真題示例&#xff1a; 一、范圍管理 1. 核心概念 項目范圍&#xff1a;為交付產品必須完成的工作…

過去十年前端框架演變與技術驅動因素剖析

一、技術演進脈絡&#xff08;2013-2023&#xff09; 2013-2015&#xff1a;結構化需求催生框架雛形 早期的jQuery雖然解決了跨瀏覽器兼容性問題&#xff08;如IE8兼容性處理&#xff09;&#xff0c;但其松散的代碼組織方式難以支撐復雜應用開發。Backbone.js的出現首次引入M…

中華傳承-醫山命相卜-梅花易數

梅花易數 靈活起卦&#xff08;如數字、聲音、外應等&#xff09;和象數結合&#xff0c;準確率可達96.8%。其起卦方式擺脫傳統龜殼、蓍草的繁瑣&#xff0c;強調直覺與靈活性。 個人決策、事件預測等 尤其在短期、具體問題上表現突出。

如何用Brower Use WebUI實現網頁數據智能抓取與分析?

作者&#xff1a;算力魔方創始人/英特爾創新大使劉力 Browser-use是一款能讓AI智能體像人類一樣操作網頁的創新工具&#xff0c;與傳統網絡爬蟲技術相比&#xff0c;Browser-use能模擬人瀏覽并操作網頁&#xff0c;在采集網站數據時&#xff0c;不會被網站反爬機制識別和封禁&…

LIMS引領綜合質檢中心數字化變革,賦能質量強國戰略

在質量強國戰略的深入推進下&#xff0c;我國綜合質檢機構迎來了前所未有的發展機遇&#xff0c;同時也面臨著諸多嚴峻挑戰。隨著檢測領域從傳統的食品藥品監督向環境監測、新材料檢測等新興領域不斷拓展&#xff0c;跨領域協同管理的復雜度呈指數級增長。作為提升產品質量的關…

簡單好用的在線工具

用AI寫了一些在線工具&#xff0c;簡介好用&#xff0c;推薦給大家&#xff0c;歡迎大家使用并提議意見。 網址&#xff1a;https://www.bittygarden.com/ 目前已有以下功能&#xff1a; MD5SM3SHAUnicode 編碼Unicode 解碼Base32 編碼Base32 解碼Base64 編碼Base64 解碼URL …

阿里云服務器搭建開源版禪道

一&#xff0c;下載地址&#xff1a;禪道11.5版本發布&#xff0c;主要完善細節&#xff0c;修復bug&#xff0c;新增動態過濾機制 - 禪道下載 - 禪道項目管理軟件 下載地址二&#xff1a; 禪道21.6.stable 實現舊編輯器撰寫的文檔無感升級至新版編輯器 - 禪道下載 - 禪道項目…

leetcode 309. Best Time to Buy and Sell Stock with Cooldown

目錄 題目描述 第一步&#xff0c;明確并理解dp數組及下標的含義 第二步&#xff0c;分析并理解遞推公式 1.求dp[i][0] 2.求dp[i][1] 3.求dp[i][2] 第三步&#xff0c;理解dp數組如何初始化 第四步&#xff0c;理解遍歷順序 代碼 題目描述 這道題與第122題的區別就是賣…

嵌入式硬件常用總線接口知識體系總結和對比

0.前言 在嵌入式工程實現中,多多少少我們都使用過總線,各種各樣的總線應用于不同場合,不同場景有不同的優勢,但是我們在作為工程師過程中在如何選擇項目合適的總線,根據什么來選?需要我們對項目全局和總線特征有所了解,本文目的就是對比多種總線的關鍵特征 我們在聊到…

數據分析處理庫Pandas常用方法匯總

目錄 一、基礎操作 1.1 創建df對象 1.1.1 讀入表格數據 1.1.2 手動創建df 1.2 .info() 1.3 df.index 1.4 df.columns 1.5 df.dtypes 1.6 df.values 1.7 .set_index() 1.8 df[xxx] 1.9 .describe() 1.10 .isin() 1.12 .where() 1.13 .query() 1.14 Series類型運算…

智慧大屏系統

延凡智慧大屏系統旨在打破數據壁壘&#xff0c;將海量、復雜的數據轉化為直觀易懂的可視化圖形和信息&#xff0c;廣泛應用于城市管理、企業運營、交通指揮、能源監控等多個領域&#xff0c;為管理者、決策者提供全面、實時、精準的信息展示和分析工具&#xff0c;助力高效決策…

樹莓派超全系列教程文檔--(32)config.txt常用音頻配置

config.txt常用音頻配置 板載模擬音頻&#xff08;3.5mm耳機插孔&#xff09;audio_pwm_modedisable_audio_ditherenable_audio_ditherpwm_sample_bits HDMI音頻 文章來源&#xff1a; http://raspberry.dns8844.cn/documentation 原文網址 板載模擬音頻&#xff08;3.5mm耳機…

23種設計模式全面解析

設計模式是解決軟件設計中常見問題的經典方案。根據《設計模式&#xff1a;可復用面向對象軟件的基礎》&#xff08;GoF&#xff09;&#xff0c;23種設計模式分為以下三類&#xff1a; 一、創建型模式&#xff08;5種&#xff09; 目標&#xff1a;解耦對象的創建過程&#x…

AI 推理框架詳解,包含如COT、ReAct、LLM+P等的詳細說明和分類整理,涵蓋其原理、應用場景及對比分析

AI 推理引擎 以下是關于 AI 推理引擎 的詳細說明&#xff0c;涵蓋其定義、類型、核心組件、技術實現、應用場景及挑戰&#xff1a; 1. 推理引擎的定義 推理引擎&#xff08;Inference Engine&#xff09;是 AI系統的核心組件&#xff0c;負責根據輸入數據、知識庫或預訓練模…

《探秘鴻蒙分布式軟總線:開啟無感發現與零等待傳輸新時代》

在數字化浪潮中&#xff0c;設備之間的互聯互通成為構建智能生態的關鍵。鴻蒙系統中的分布式軟總線技術&#xff0c;宛如一座橋梁&#xff0c;讓各種智能設備緊密相連。尤其是其實現的設備間無感發現和零等待傳輸功能&#xff0c;更是為用戶帶來了前所未有的便捷體驗&#xff0…

JDBC 與 MyBatis 詳解:從基礎到實踐

目錄 一、JDBC 介紹 二、使用 JDBC 查詢用戶信息 三、ResultSet 結果集 四、預編譯 SQL - SQL 注入問題 五、預編譯 SQL - 性能更高 六、JDBC 增刪改操作 插入數據&#xff1a; 更新數據&#xff1a; 刪除數據&#xff1a; 七、MyBatis 介紹 八、MyBatis 入門程序 引…