輕松拿下指針(5)

文章目錄

  • 一、回調函數是什么
  • 二、qsort使用舉例
  • 三、qsort函數的模擬實現

一、回調函數是什么

回調函數就是?個通過函數指針調?的函數。
如果你把函數的指針(地址)作為參數傳遞給另?個函數,當這個指針被?來調?其所指向的函數
時,被調?的函數就是回調函數。回調函數不是由該函數的實現?直接調?,?是在特定的事件或條件發?時由另外的??調?的,?于對該事件或條件進?響應。
第指針(4)中我們寫的計算機的實現的代碼中 switch 語句代碼是重復出現的,其中雖然執?計算的邏輯是區別的,但是輸?輸出操作是冗余的,有沒有辦法,簡化?些呢?
int main() 
{int input = 0;int x = 0;int y = 0;int ret = 0;do{menu();printf("請選擇:");scanf("%d", &input);switch (input){case 1:printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);break;case 2:printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("%d\n", ret);break;case 3:printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("%d\n", ret);break;case 4:printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = Div(x, y);printf("%d\n", ret);break;case 0:printf("退出游戲\n");break;default:printf("選擇錯誤,請重新選擇\n");}}while (input);return 0;
}

因為 switch 語句的代碼,只有調?函數的邏輯是有差異的,我們可以把調?的函數的地址以參數的形式傳遞過去,使?函數指針接收,函數指針指向什么函數就調?什么函數,這?其實使?的就是回調函數的功能。
那具體怎么簡化這段代碼呢?
能不能把這四段代碼分裝成一個函數,把四個問題都解決。
設計一個 “中間商” 函數Calc():
void Calc(int(*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}

最后改造后的結果:

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 menu()
{printf("********************************\n");printf("******   1. add    2. sub  *****\n");printf("******   3. mul    4. div  *****\n");printf("******   0. exit           *****\n");printf("********************************\n");
}void Calc(int(*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("請輸入兩個整數:\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do{menu();printf("請選擇:");scanf("%d", &input);switch (input){case 1:Calc(Add);break;case 2:Calc(Sub);break;case 3:Calc(Mul);break;case 4:Calc(Div);break;case 0:printf("退出游戲\n");break;default:printf("選擇錯誤,請重新選擇\n");}} while (input);return 0;
}
  • (簡單來說:把一個函數的地址傳遞給了函數指針,在這個函數內部通過函數指針去調用他所指向的函數,這種通過函數指針調用函數的方式,被調用的函數就是回調函數。)

二、qsort使用舉例

  • qsort是C語言中的一個庫函數,這個函數是用來對數據進行排序的,對任意類型的數據都能進行排序。(quiick sort 底層使用的快速排序的思想)

void qsort(void* base, //指向待排序數組的第一個元素的指針size_t num, //base指向數組中的元素個數size_t size,//base指向的數組中一個元素的大小,單位是字節int (*cmp)(const void*, const void*) //函數指針 - 傳遞函數的地址//);

而在 compar 函數中的實現結果如下:

int compareMyType (const void * a, const void * b)
{if ( *(MyType*)a <  *(MyType*)b ) return -1;if ( *(MyType*)a == *(MyType*)b ) return 0;if ( *(MyType*)a >  *(MyType*)b ) return 1;
}

2.1 使用qsort函數排序整型數據?

如果想使用 qsort 函數排序整型類型數據,就得提供一個比較 2 個整型的比較函數:

int cmp_int(const void* p1, const* p2)
{return(*(int*)p1 - *(int*) p2);
}int main()
{int arr[10] = { 3,1,5,6,9,8,7,2,4,10 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

意思是,如果想要比較兩個“字符串”,兩個“結構體”等,就提供相應的比較函數,這樣qsort就實現了可以比較任何類型數據的功能了。

再舉個例子:

2.2 使用qsort排序結構數據

首先創建一個結構體

#include<stdio.h>
#include<string.h>struct Stu //學?
{char name[20];//名字int age;//年齡
};int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);}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 (arr[j] > arr[j + 1])if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void test3()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);}void test4()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);}int main()
{test3();test4();return 0;
}
struct stu
{char name[20];int age;
};
1.?假設按照姓名來比較
int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//因為 p1 的類型是 void*,所以我們要把它強制類型轉換成(struct Stu*),注意這個轉換是臨時的,所以要用括號括起來((struct Stu*)p1)->name。
}void test1()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);}
2.?假設按照年齡來比較
int cmp_stu_by_age(const void* p1, const void* p2)
{ return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}void test1()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);}

最后進入主函數調試,分別對 arr 進行監控

int main()
{test1();test2();return 0;
}

(注意:strcmp比較的不是字符串長度,而是對應位置上字符的大小!!)

三、qsort函數的模擬實現

在指針(3)的講解中,我們了解到冒泡排序的算法及引用,并定義了代碼 bubble_sort 函數。而了解了qsort這個庫函數后,我們能否可以將 buble_sort 函數改造成通用的算法,可以排序任意類型的數據?

答案肯定是可以的,可以通過 qsort 的模仿實現。

前面講到 qsort 是底層使用的快速排序,而我們自己定義的? bubble_sort 是通過冒泡排序的思路實現排序,不會有沖突。

代碼如下:

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 (arr[j] > arr[j + 1])//比較兩個元素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 的形參描寫的過程:

? ? ?1.不清楚要排序的數據的類型,所以用 void *base接收;

? ? ?2.base指向的數組的大小,單位是字節,用 size_t(無符號整型)?接收;

? ? ?3.base指向的數組中一個元素的大小,單位是字節,但是我們不清楚將來的數據類型是什么,所以我們可以用寬度 width 接收;

? ? ?4.void*類型指針可以接受任何類型的地址,可以寫成?(*cmp)(const void* p1, const void* p2)) ,返回類型是 int。

  • bubble_sort 中的邏輯算法:

? ? ?1.首先我們清楚在冒泡排序中的趟數,和每一趟中所需要兩兩對比的對數是不會改變的,不論數據類型是怎么樣,都是采取這種對比過程。

? ? ?2.(1)需要改變就是 if ( ) 中的條件和交換過程 利用 cmp 的返回值是否大于零,如果大于零說明前者大于后者則進行交換(假設是升序排列)。如果我們要使用冒泡排序的思路,就要解決?arr[j] 和?arr[j + 1] 這兩個元素的地址傳達。

? ? ? ? (2)而我們只知道首元素 base 的地址以及一個元素的寬度 width,但具體的大小不清楚。假設是一組整型數組 int arr[10] = { 3,1,5,6,9,8,7,2,4,10 } ,width 就等于 4,所以我們可以把 base 強制類型轉換成 (char*)base,當指向首元素地址 3時為?(char*)base+0,指向下個元素地址 1時為(char*)base+width*(0+1),那么就可以寫成:

? ? ? ? ? ? ? ? ? ? ? ?if(cmp((char*)base+j*width, (char*)base+(j+1)*width)>0)

? ? ? ? (3)接下里就進入交換:

? ? ? ? ? ? ? ? ? Swap((char*)base + j * width, (char*)base + (j + 1) * width, width)

buf1 和 buf2相差的是一個 width(4個字節)那交換的時候是否就可以創建一個 int tmp = 0 進行交換呢?在void Swap函數中并不知道buf1.2是什么類型,只知道兩者之間相差一個元素是 4 個字節,所以只能一個字節一個字節得交換,四對字節分別交換,兩個整型就交換成功了。

  • 最后我們把上述的結構體數據利用 bubble_sort 排序看是否成功:
#include<stdio.h>
#include<string.h>struct Stu //學?
{char name[20];//名字int age;//年齡
};int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);}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 (arr[j] > arr[j + 1])if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}
void test3()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);}void test4()
{struct Stu arr[] = { {"zhangsan", 20},{"lisi",35},{"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);}int main()
{test3();test4();return 0;
}

最終監視一下 arr 中的地址內容:

1.按姓名排序

2.按年齡排序

未完待續~~

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

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

相關文章

計算模型的邊界

https://github.com/libigl/libigl.git 這是幾何計算庫&#xff0c;可以計算出模型的邊界 #define _CRT_SECURE_NO_WARNINGS#include <iostream>#include <igl/boundary_loop.h>#include <igl/list_to_matrix.h>int main(){std::vector<std::vector<…

肺部營養“救星”,讓每次呼吸更自由

?#肺科營養#朗格力#班古營養#復合營養素#肺部營養# 正常的健康人,每天自由幸福的呼吸。但是對于肺病患者來說,特別是慢阻肺人群,每一次呼吸都可能是一場挑戰,每一口氣都顯得彌足珍貴。 肺病患者號稱沉默的“呼吸殺手”,它雖然沉默,但不代表它沒能力,除了引起肺功能下降,氧氣…

云商店如何讓更多企業摘到技術普惠的“果實”?

文 | 智能相對論 作者 | 沈浪 現階段&#xff0c;越是工業體系發達的地區&#xff0c;越需要加速技術普惠的步伐。比如&#xff0c;在蘇州&#xff0c;華為云就在聯合當地政府、企業伙伴打造以華為云云商店為重要鏈接的智能化商業增長底座。 華為云云商店以“電商式”的購物…

Git學習——遷移單一倉庫至其他代碼托管平臺

Git學習——遷移單一倉庫至其他代碼托管平臺 簡介流程總結 簡介 因需遷移單一代碼倉庫至其他代碼托管平臺&#xff0c;要遷移的包括倉庫內容以及所有歷史記錄和推送日志。 本文中的方法同樣適用于在同一代碼托管平臺中克隆倉庫。 流程 1. 創建新倉庫&#xff1a; 在目的平臺…

軟件需求規格文檔 (SRS) 模版

文章目錄 軟件需求規格文檔 (SRS) - 范例1. 引言1.1 目的1.2 范圍1.3 定義、縮寫和術語1.4 參考文獻1.5 總體描述 2. 系統概述2.1 系統環境2.2 系統功能概述2.3 用戶特性2.4 假設與約束 3. 功能需求3.1 用戶身份驗證模塊3.1.1 總體概述3.1.2 具體需求3.1.2.1 登錄功能描述3.1.2…

OpenAI春季發布會-免費多模態GPT4O-簡介

前言 2024.5.14&#xff0c;OpenAI宣布即將發布一款性能更為強大的大模型GPT4o&#xff0c;雖然沒有爆出些超級酷炫無敵吊炸天的新玩意&#xff0c;但是這次的多模態模型&#xff0c;大家可以免費用了~~&#xff08;但是&#xff09; 雖然是免費使用&#xff0c;但官方發布會上…

逆向學習記錄--第一天

NSSCTF工坊逆向綜合基礎第二題 考查知識點&#xff1a;ida的使用與編程能力 wp&#xff1a; 運行一下&#xff0c;沒有東西&#xff1b; 查殼是64位&#xff0c;沒有殼 直接ida打開 代碼解析&#xff1a;輸入flag&#xff0c;對flag進行用key進行輪換之后再加12&#xff…

鴻蒙 DevEcoStudio:用戶名密碼獲取保存

【使用首選項實現用戶名密碼保存獲取】 打開src/main/ets/entryability路徑下的EntryAbility.ts文件 在 export default class EntryAbility extends UIAbility {onCreate(want, launchParam) {hilog.info(0x0000, testTag, %{public}s, Ability onCreate);下邊添加內容&…

C++:左值(引用)右值(引用)

〇、前言 本文會討論C中的左值&#xff0c;右值&#xff0c;左值引用&#xff0c;右值引用&#xff0c;以及會理清它們之間的關系。 一、左值與右值 &#xff08;一&#xff09;概述 1. 左值是一般指表達式結束后依然存在的持久化對象。右值指表達式結束時就不再存在的臨時…

334_C++_std::bind中使用shared_from_this()

std::bind(&HttpClient::getPwd, shared_from_this(), std::placeholders::_1, std::placeholders::_2);[ HttpClient繼承自NetObj,NetObj是父類,NetObj受到std::shared_pt

分析 vs2019 c++ 中的 decltype 與 declval

&#xff08;1&#xff09; decltype 可以讓推斷其參數的類型。按住 ctrl 點擊 decltype &#xff0c;會發現無法查閱 其定義 &#xff1a; &#xff08;2&#xff09; 但 STL 庫里咱們可以查閱函數 declval 的 定義&#xff0c;很短&#xff0c;摘抄如下&#xff1a; templat…

【氮化鎵】高溫GaN HEMTs大信號模型——ASM-HEMT

這篇文章的標題是《An ASM-HEMT for Large-Signal Modeling of GaN HEMTs in High-Temperature Applications》&#xff0c;由Nicholas C. Miller等人撰寫&#xff0c;發表于2023年9月29日。文章的主要內容是關于一種適用于高溫應用的GaN HEMTs&#xff08;高電子遷移率晶體管&…

Java 高級面試問題及答案1

Java 高級面試問題及答案 問題1: 請解釋Java中的垃圾回收機制&#xff0c;并描述其工作原理。 答案&#xff1a; Java中的垃圾回收&#xff08;Garbage Collection, GC&#xff09;是一種自動內存管理機制&#xff0c;用于識別和回收不再使用的對象&#xff0c;從而釋放內存資…

使用System.Drawing繪制基本幾何圖形

1.使用System.Drawing繪制一個正方形 using System; using System.Drawing; using System.Windows.Forms;public partial class MyForm : Form {public MyForm(){// 你可以在這里設置Form的雙緩沖&#xff0c;以避免繪制時出現的閃爍 this.DoubleBuffered true;}protected o…

LeetCode 每日一題 ---- 【1553.吃掉 N 個橘子的最少天數】

LeetCode 每日一題 ---- 【1553.吃掉 N 個橘子的最少天數】 1553.吃掉N個橘子的最少天數方法&#xff1a;記憶化搜索 1553.吃掉N個橘子的最少天數 方法&#xff1a;記憶化搜索 前兩天給樹澆水&#xff0c;原來澆的是橘子樹哇 今天直接來了個大的【困難】 class Solution {Ma…

Linux——緩沖區

一、問題引入 我們先來看看下面的代碼&#xff1a;我們使用了C語言接口和系統調用接口來進行文件操作。在代碼的最后&#xff0c;我們還使用fork函數創建了一個子進程。 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/wait.h&…

將jar打包成exe可安裝程序,并在html頁面喚醒

一、exe4j將jar打包成exe 1.exe4j下載 下載地址&#xff1a;https://www.ej-technologies.com/download/exe4j/files 2.exe4j打包jar 2.1. welcome 可以選擇歷史配置&#xff0c;新增則直接下一步 2.2. project type選擇“jar in exe” mode 2.3. application info設置應用…

【接口測試_03課_-接口自動化思維梳理及Requests庫應用】

一、通過代碼&#xff0c;實現Jmeter 1、項目要放在虛擬環境里面&#xff0c;解釋器要使用虛擬環境的 上面是虛擬環境&#xff0c;下面是系統環境。2選一 venv目錄 查看當前虛擬環境已存在的依賴包 2、安裝Requests依賴包 1&#xff09;安裝命令 pip install requests 如果…

防火墻技術的演進,什么是下一代防火墻(NGFW)?

防火墻技術的演進 防火墻技術的演進經歷了不同階段&#xff0c;從包過濾防火墻到狀態檢測防火墻&#xff0c;再到集成多種安全功能的UTM&#xff08;統一威脅管理&#xff09;設備&#xff0c;最終發展到具備應用識別能力的NGFW&#xff08;下一代防火墻&#xff09;。 包過濾…

DTAS 尺寸公差分析及尺寸鏈計算-建模神器 — 用戶DIY裝配

工業互聯網&#xff08;工業4.0) 是未來智能制造的核心&#xff0c;工業軟件是智能制造的靈魂。 相關工業軟件及系統的自主研發是智能制造和質量升級轉型亟需解決的卡脖子環節&#xff0c;而公差分析軟件系統是前期質量研發精準設計、降本增效的關鍵。 數字化時代&#xff0…