【C語言】指針(5)

前言:上篇文章的末尾我們使用了轉移表來解決代碼冗余的問題,那我們還有沒有什么辦法解決代碼冗余呢?有的這就是接下來要說的回調函數。
往期文章:
指針1
指針2
指針3
指針4

文章目錄

  • 一,回調函數
  • 二,qsort實現快速排序
    • 1,void*指針
  • 三,qsort的模擬實現

一,回調函數

先來回顧一下上篇文章末尾的內容,寫一個模擬計算的代碼:

#include <stdio.h> 
int add(int a, int b) 
{ return a + b; 
}
int sub(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("*******   0:exit   ******\n");printf("*************************\n");printf("請選擇:");scanf("%d", &input); switch (input) {case 1: printf("輸?操作數:"); scanf("%d %d", &x, &y); ret = add(x, y); printf("ret = %d\n", ret); break; case 2: printf("輸入操作數:"); scanf("%d %d", &x, &y); ret = sub(x, y); printf("ret = %d\n", ret); break; case 0: printf("退出程序\n"); break; default: printf("選擇錯誤\n"); break; } } while (input); return 0; 
}

我們可以看到代碼是非常冗余的,想要解決冗余的問題方法一在我們上一篇文章指針4(轉移表)。方法二我們使用回調函數來實現。
那什么是回調函數呢?

回調函數說的是,如果你把函數的指針(地址)作為參數傳遞給另外一個函數時,當這個指針被用來調用其所指向的函數時,被掉用的函數就稱為回調函數。

我們給出具體的代碼:

#include<stdio.h>
int add(int x, int y)
{return x + y;
}
int sub(int x, int y)
{return x - y;
}
void clac(int (*pf)(int, int))
{int x = 0, y = 0;printf("請輸入兩個數:");scanf("%d %d", &x, &y);int ret = (*pf)(x, y);printf("計算結果為:%d\n", ret);
}
int main()
{int x, y;int input = 1;int ret = 0;do {printf("*************************\n");printf("*******1:add 2:sub ******\n");printf("*******   0:exit   ******\n");printf("*************************\n");printf("請選擇:");scanf("%d", &input);switch (input){case 1:clac(add);break;case 2:clac(sub);break;case 0:printf("退出程序\n");}} while (input);
}

通過上面的兩段代碼對比我們有兩個改進之處:
一,將case語句中那些重復性的語句封裝到了一個新建的函數中。
二,函數調用發生了變化,從原來的直接調用add函數sub函數變成了先調用clac函數再去調用add函數sub函數。這種函數稱之為回調函數。

看明白這兩點相信已經不難理解回調函數了,但是還有一點需要注意:回調函數不是由該函數的實現方直接調用,而是在特定的事件或條 件發生時由另外的一方調用的,用于對該事件或條件進行響應。
下面畫一張圖讓你更好的理解:
在這里插入圖片描述


二,qsort實現快速排序

在前面的文章中我們介紹了冒泡排序,這次我們來介紹一些qsort。q即quick快速的,sort是排序的意思。所以qsort就是快速排序俗稱快排。
那怎么使用qsort來實現快速排序呢?首先我們先得了解一些qsort:

在這里插入圖片描述
![在這里插入圖片描述

void qsort( 
void *base, 
size_t num, 
size_t width, 
int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

通過專業文獻對qsort的描述我們知道qsort共有4個參數第一個參數

  1. void *base 是代表要傳入的目標數組名即所需要排序的數組名。
  2. size_t num 是代表要傳入的數組內有多少個元素。
  3. size_t width 是代表要傳入的數組內元素的大小。
  4. int (*compare )(const void *elem1, const void *elem2 ) 是一個函數指針,函數指針的兩個參數 類型都為void*也就是說在第四個參數中我們要傳入一個函數且這個函數具有比較elem1elem2這兩個元素大小的功能。

我們寫一個排序整型數組的代碼來給大家舉例,讓大家能更好的了解qsort的使用方法:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
/*
void qsort(void* base, //第一個參數為需要比較的數組的首地址size_t num,  //第二個參數為該數組的元素個數size_t width, //第三個參數為該數組每個元素的大小int(__cdecl* compare)(const void* elem1, const void* elem2));//第四個參數為函數指針,及傳入一個能比較數組內部元素大小的函數的地址
*///實現比較兩個函數大小的函數
int comp_int(const void* elem1, const void* elem2)
{return *(int*)elem1 - *(int*)elem2;//void*的指針不能直接使用 要強轉后再解引用才能使用 //elem1>elem2  返回大于零的數//elem==elem2 返回0//elem1<elem2  返回小于零的數
}
//打印整型數組
void print(int* P_arr, int sz)//一維數組傳參傳過去的是數組的首地址
{int i = 0;for (i = 0;i < sz;i++){printf("%d ", *(P_arr + i));}
}
//排整型的數組
int test1()
{int arr1[10] = { 1,4,3,2,6,5,8,7,9,10 };int sz = sizeof(arr1) / sizeof(arr1[0]);qsort(arr1, sz, sizeof(arr1[0]), comp_int);//print(arr1, sz);
}
int main()
{//寫一個test1來測試qsort排序整型數組test1();return 0;
}

我們來分析代碼的含義:

在這里插入圖片描述

1,void*指針

上面諸多地方提到了void*指針,下面我們給出解釋:

在指針類型中有?種特殊的類型是 void* 類型的,可以理解為無具體類型的指針(或者叫泛型指 針),這種類型的指針可以用來接受任意類型地址。但是也有局限性, void*類型的指針不能直接進行指針的±整數和解引用的運算。

#include <stdio.h> 
int main() { int a = 10; int* pa = &a; char* pc = &a; return 0;
}

在上?的代碼中,將?個int類型的變量的地址賦值給?個char*類型的指針變量。編譯器會警告,是因為類型不兼容。而使用void*類型就不會有這樣的問題。
我們再看看void*的指針接收別的類型的指針:

#include <stdio.h> 
int main() 
{ int a = 10; void* pa = &a; void* pc = &a; *pa = 10; *pc = 0; return 0; 
}

在這里插入圖片描述

這里我們可以看到, void* 類型的指針可以接收不同類型的地址,但是無法直接進行指針運算。 那么 void* 類型的指針到底有什么用呢? ?般 void* 類型的指針是使?在函數參數的部分,用來接收不同類型數據的地址,這樣的設計可以實現泛型編程的效果。

理解了void*類型,以及qsort如何使用了以后,我們就可以試著去排序一些其他類型的數據了。代碼內容在下載文件處取噢。

熟練了使用qosrt來排序各種類型的數據后接下來我們就來模仿著造一個qosrt函數。

三,qsort的模擬實現

我們之前學過了冒泡排序并且知道冒泡排序有一個缺點就是只能排序固定類型的數據,而qsortt能排序任意類型的數據那我們能不能使用冒泡排序的方式來模擬實現qsort快速排序呢?答案是肯定的。
我們先寫一個冒泡排序,然后再看看哪些地方需要修改:

#include<stdio.h>
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]=tmp;}}}
}
void print_arr(int arr[],int sz)
{int i=0;for(i=0;i<sz;i++){printf("%d ",arr[i]);}printf("\n");
}
void test()
{int arr[10]={1,3,5,7,9,2,4,6,8,0};int sz=sizeof(arr)/sizeof(arr[0]);bubble_sort(arr,sz);print_arr(arr,sz);
}
int main()
{test();return 0;
}

那如何知道那些地方需要修改呢?通過與qsort的對比我們可以得出以下幾個地方需要改造:在這里插入圖片描述
那我們就先來改造參數部分:void bubble_sort(void*base,int sz,int width,int (*cmp)(const void*e1,const void*e2)) 改造之后我們發現多了兩個參數,一個是 width 代表單個元素大小;一個是 int (*cmp)(const void*e1,const void*e2)
這個函數指針。

其中一個修改的地方是從原來接受int類型的數組改為了void*類型原因是方便接受任意類型的數組。

其次我們來修改比較部分,上面分析了使用一個函數指針指向一個函數然后通過函數的返回值來得到e1和e2這兩個元素的大小關系,這也是為什么函數指針的返回類型是int的原因。

int comp(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}

比較函數我們依然可以這樣寫但是要注意一個問題,現在是我們模仿qsort的邏輯,所以在bubble_sort這個函數里邊就會涉及一個傳參的問題,下面我們著重來探究傳參問題:在這里插入圖片描述

if(arr[j]>arr[j+1])——————>if(comp((char*)base+j*width,(chr*)base+(j+1)*width)>0)
//注意base就是arr

解決了參數問題,解決了傳參問題接下來就是交換數據的問題了,如果不滿足我們的升序要求則需要交換那怎么交換呢?通過上面的比較得出需要交換那我們既然已經得到了要交換的兩個元素的地址,就不妨再封裝一個函數來專門去交換元素的內容。
在這里插入圖片描述

#include<stdio.h>
#include<stdlib.h>
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++;}
}

解決完交換的問題我們就可以給出所有的代碼啦:

int bubble_cmp(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
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++;}
}
int bubble_sort(void* base, int sz, int 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 (bubble_cmp((char*)base+j*width,(char*)base+(j+1)*width)>0){swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}}}
}
void test2()
{int arr[10] = { 1,3,5,7,9,2,4,6,8,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), bubble_cmp);print(arr, sz);
}
int main()
{test2();//使用冒泡排序來模擬qsortreturn 0;
}

好了以上就是本章的全部內容啦!
感謝能夠看到這里的讀者,如果我的文章能夠幫到你那我甚是榮幸,文章有任何問題都歡迎指出!制作不易還望給一個免費的三連,你們的支持就是我最大的動力!
在這里插入圖片描述

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

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

相關文章

【python】網頁批量轉PDF

安裝wkhtmltopdf 網站&#xff1a;wkhtmltopdf wkhtmltopdf http://www.baidu.com/ D:website1.pdf 安裝pdfkit庫 pip install pdfkit 批量轉換代碼 import os import pdfkit path_wkthmltopdf rE:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe config pdfkit.configu…

游戲引擎學習第113天

倉庫:https://gitee.com/mrxiao_com/2d_game_2 黑板&#xff1a;優化的基本過程 在游戲編程中&#xff0c;優化是一個非常重要的學習內容&#xff0c;尤其是想要成為專業開發者時。優化的核心是理解代碼的執行速度&#xff0c;以及如何提升其性能。在這個階段&#xff0c;已經…

通義靈碼AI程序員

通義靈碼是阿里云與通義實驗室聯合打造的智能編碼輔助工具&#xff0c;基于通義大模型技術&#xff0c;為開發者提供多種編程輔助功能。它支持多種編程語言&#xff0c;包括 Java、Python、Go、TypeScript、JavaScript、C/C、PHP、C#、Ruby 等 200 多種編碼語言。 通義靈碼 AI…

SeaTunnel社區「Demo方舟計劃」首期活動上線—— MySQL CDC實時同步至PostgreSQL實戰

引言 凌晨2點&#xff0c;某電商公司的數據工程師小李正對著屏幕抓狂——業務部門臨時要求將MySQL的訂單表實時同步到PostgreSQL進行分析&#xff0c;眾所周知&#xff0c;在數據驅動的業務場景中&#xff0c;異構數據源同步是高頻剛需。 以MySQL到PostgreSQL的CDC同步為例&a…

iNeuOS工業互聯網操作系統,民爆遠程運維平臺案例

iNeuOS工業互聯網操作系統,民爆遠程運維平臺案例 目 錄 1. 概述... 2 2. iNeuOS在民爆生產廠區和北京運維中心配置... 3 1.1 生產廠區配置... 3 1.2 運維中心配置... 7 1. 概述 針對本項目進行初步調研,項目的總體需求為滿足新建…

利用websocket檢測網絡連接穩定性

瀏覽器中打開F12&#xff0c;控制臺中輸入以下內容 > 回車 > 等待結果 連接關閉 表示斷網 let reconnectDelay 1000; // 初始重連間隔 let pingInterval null; let socketManuallyClosed false; // 標志是否手動關閉function createWebSocket() {if (socketManuallyCl…

Unity shader glsl著色器特效之 模擬海面海浪效果

一個簡單的海浪效果&#xff0c;通過波的疊加實現水面起伏的動效&#xff0c;根據波峰斜率來為浪花著色&#xff0c;再根據法線貼圖和水花貼圖來和調整uv的平滑移動來增強海浪移動的細節。如果需要更逼真的效果可以考慮在滿足浪花觸發的地方添加粒子系統 前置效果圖 因為是很久…

智能經濟與個人智能助理有什么發展

智能經濟與個人智能助理有什么發展 技術融合創新 研究個人助理與新興技術&#xff08;如量子計算、邊緣計算&#xff09;融合&#xff0c;分析對智能經濟的推動作用。探索量子計算提升數據處理速度&#xff0c;邊緣計算降低延遲&#xff0c;提升個人助理性能的機制&#xff0…

spring日志

前言 入門 這些就是日志 現在開始使用一下 spring是集合了日志的 注意選這個 這樣我們就創建好了一個日志對象了 我們就可以這樣打印日志了 日志和普通的打印消息相比&#xff0c;區別就是多個一些時間之類的消息 從左到右分別是時間&#xff0c;級別&#xff0c;PID&#x…

整合Salesmart/WhatsApp、開源Odoo模塊和Deepseek AI能力,實現針對國外客戶的智能客服和個性化推薦服務

一、項目背景 本文提出了一套針對軟管制造公司的智能客服與個性化推薦系統實施方案&#xff0c;旨在通過整合開源Odoo模塊、Salesmart/WhatsApp以及Deepseek AI能力&#xff0c;打造一個724小時不間斷服務的智能化平臺&#xff0c;專注于服務國外客戶。方案圍繞實現不間斷服務…

Java中JDK、JRE,JVM之間的關系

Java中的JDK、JRE和JVM是三個核心概念&#xff0c;其關系可概括為JDK > JRE > JVM&#xff0c;具體如下&#xff1a; 一、定義與作用 JDK&#xff08;Java Development Kit&#xff09; 定義&#xff1a;Java開發工具包&#xff0c;用于開發和編譯Java程序。包含內容&…

用C++ Qt實現安卓電池充電動效 | 打造工業級電量控件

一、為什么需要自定義電池控件&#xff1f; 在工業控制、車機系統、智能硬件等領域的UI開發中&#xff0c;電池狀態顯示是高頻出現的UI組件。通過實現一個支持顏色漸變、動態充電動畫、警戒閾值提示的電池控件&#xff0c;開發者可以系統掌握以下核心能力&#xff1a; Qt繪圖…

Django+Vue3全棧開發實戰:從零搭建博客系統

文章目錄 1. 開發環境準備2. 創建Django項目與配置3. 設計數據模型與API4. 使用DRF創建RESTful API5. 創建Vue3項目與配置6. 前端頁面開發與組件設計7. 前后端交互與Axios集成8. 項目優化與調試9. 部署上線10. 總結與擴展10.1 項目總結10.1.1 技術棧回顧10.1.2 項目亮點 10.2 擴…

Django 5實用指南(五)模板系統

Django5的模板系統是其核心功能之一&#xff0c;允許開發者將動態數據嵌入到HTML模板中&#xff0c;并根據不同的業務需求渲染頁面。Django模板系統基于 Django模板語言&#xff08;DTL&#xff09;&#xff0c;它提供了一些強大的功能&#xff0c;如模板標簽、過濾器、條件語句…

uni-app開發app時 使用uni.chooseLocation遇到的問題

問題一&#xff1a;不顯示 問題二&#xff1a;選擇地址列表一直在加載中 因為 uni-app 接口文檔 中已經說明&#xff0c;使用騰訊的話需要開啟云服務&#xff0c;具體可看官網&#xff0c;這就是為什么使用時直接不顯示的原因&#xff0c;所以我使用的高德&#xff0c;但又出現…

推薦系統-排序模型

本次學習的重點是FM系列和WideNDeep系列。其實這兩個模型是存在因果關系的。從最初的LR模型開始&#xff0c;因為缺失高效的特征交互方式&#xff0c;產生了FM模型&#xff0c;即通過向量內積代替特征之間的兩兩交互的參數。最后DNN的引入可以建模更高階的特征。但是DNN如何與F…

體驗用ai做了個python小游戲

體驗用ai做了個python小游戲 寫在前面使用的工具2.增加功能1.要求增加視頻作為背景。2.我讓增加了一個歡迎頁面。3.我發現中文顯示有問題。4.我提出了背景修改意見&#xff0c;歡迎頁面和結束頁面背景是視頻&#xff0c;游戲頁面背景是靜態圖片。5.提出增加更多游戲元素。 總結…

c#爬取數據并解析json

安裝 Newtonsoft.Json Install-Package Newtonsoft.Json代碼 HttpClient client new HttpClient();// 獲取網頁內容HttpResponseMessage response client.GetAsync("https://opentdb.com/api.php?amount10&category18&difficultyeasy&typemultiple"…

計算機畢業設計Python農產品推薦系統 農產品爬蟲 農產品可視化 農產品大數據(源碼+LW文檔+PPT+講解)

溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的學長聯系方式的名片&#xff01; 溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的學長聯系方式的名片&#xff01; 溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的學長聯系方式的名片&#xff01; 作者簡介&#xff1a;Java領…

【分布式理論13】分布式存儲:數據存儲難題與解決之道

文章目錄 一、數據存儲面臨的問題二、RAID磁盤陣列的解決方案1. RAID概述2. RAID使用的技術3. RAID的代表性等級 三、分布式存儲的新思路1. 分布式存儲背景與特點2. 分布式存儲的組成要素 一、數據存儲面臨的問題 在單機系統時代&#xff0c;當數據量不斷增加、硬盤空間不夠時…