C語言指針相關知識(第四篇章)(非常詳細版)

文章目錄

  • 前言
  • 一、什么是回調函數
  • 二、qsort函數的介紹(默認升序排序)
  • 三、qsort函數的模擬實現(通過冒泡排序)
  • 總結


前言

本文介紹了回調函數,qsort函數的使用,以用冒泡排序來模擬實現qsort函數


提示:以下是本篇文章正文內容,下面案例可供參考

一、什么是回調函數

  • 前面的博客里面我介紹了函數指針變量的相關概念,而回調函數就是通過一個函數指針調用的函數。進一步說,如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,被調用的函數就是回調函數,注意哈,回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生的時候由另一方調用噠,用于對該事件或條件進行響應。
  • 我們使用回調函數其實可以簡化代碼,省去一些冗余重復的操作
    以下是實現一個計算器的代碼,我們在沒有使用回調函數之前:
#include<stdio.h>
int add(int a, int b)//加法{return a + b;}int sub(int a, int b)//減法{return a - b;}int mul(int a, int b)//乘法{return a*b;}
int div(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("  3:mul        4:div\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 3:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("輸?操作數:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;//這里之前的代碼較為冗余,重復地方過多case 0:printf("退出程序\n");break;default:printf("選擇錯誤\n");break;}} while (input);return 0;}

在實現計算器的過程中,我們可以發現在case語句中輸入輸出較為冗余,重復次數過多,這里我們就可以用回調函數來簡化代碼,設置一個操作函數,參數為函數指針變量來簡化代碼。具體操作如下:

#include<stdio.h>
int add(int a, int b)//加法{return a + b;}int sub(int a, int b)//減法{return a - b;}int mul(int a, int b)//乘法{return a*b;}
int div(int a, int b)//除法{return a / b;}
void calc(int(*pf)(int,int))
{
int ret = 0;
int x,y;
printf("輸入操作數:");
scanf("%d %d",&x,&y);
ret = pf(x,y);//這里我們通過函數指針調用相關函數,所被調用的函數即為回調函數。
printf("ret=%d\n",ret);
}
int main(){int x, y;int input = 1;int ret = 0;do{//菜單:
printf("*************************\n");printf("  1:add        2:sub\n");printf("  3:mul        4:div\n");printf("*************************\n");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");break;}} while (input);return 0;}

很明顯我重新定義了一個函數calc,而其參數為函數指針變量,我們將我們要執行的加減乘除函數傳遞到函數中,就可以減少重復的輸入和輸出代碼,從而做到了簡化代碼的功效。

二、qsort函數的介紹(默認升序排序)

  • qsort函數是我們C語言庫中用來專門用來排序的庫函數(頭文件為:stdlib.h)
  • 定義聲明為:
  • void qsort (void* base, size_t num, size_t size, int (compar)(const void,const void*));
  • base代表待排序序列,num,為序列中元素個數,size,代表每個元素所代表的字節大小,最后一個參數為函數指針類型,是一個比較函數,用來闡述比較規則的。
  • 對于最后一個參數函數指針類型的參數,我們通過它的返回值來確定具體那個元素在前,那個元素在后:
    若返回值<0,則第一個指針指向的元素在前,第二個指針指向的元素在后;若返回值=0,則默認第一個指針指向的元素在前,第二個指向的元素在后;若返回值>0,則第一個指針指向的元素在后,第二個指針指向的元素在前。
  • 使用qsort函數來排序整型數據:
    代碼顯示:
#include<stdio.h>
#include<stdlib.h>
int int_cmp(const void *p1,const void*p2)
//實現泛式編程,我們定義void*指針,這樣就可以接受任何類型的數據。后面只需要強制類型轉換成我們所需要的數據類型即可。
{
return (*(int*)p1-*(int *)p2);
}
int main()
{
int arr[]={1,3,5,7,9,2,4,6,8,0};
int i =0;
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(int),int_cmp);//根據需要傳遞相應的參數。
for(int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
printf("%d ",arr[i] );
}
printf("\n");
}
  • 使用qsort函數排序結構數據
  • 我們來進行結構體的排序以學生結構體為例,我們進行分別以學生的名字為依據和學生的年齡為依據進行比較。
    以年齡為依據進行排序:
#include<stdio.h>
#include<stdlib.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 main()
{
struct Stu arr[]={{"zhangsan,20"},{"lisi,30"},{"wangwu,35"}};//這里我們定義結構體序列
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(s[0]),cmp_stu_by_age);
}
return 0;

這里的排序結果我們通過調試來顯示:
排序前:
在這里插入圖片描述
排序后:
在這里插入圖片描述
以名字為依據進行排序:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>//我們需要調用字符串函數strcmp函數進行字符串的比較
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 arr[]={{"zhangsan,20"},{"lisi,30"},{"wangwu,35"}};//這里我們定義結構體序列
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(s[0]),cmp_stu_by_age);
}
return 0;

我們通過調試來顯示排序結果:
排序前:

在這里插入圖片描述
排序后:
在這里插入圖片描述
這里strcmp字符串比較函數的返回值正好符合我們qsort對于比較函數返回值的要求,二者可謂是不謀而合呀。

三、qsort函數的模擬實現(通過冒泡排序)

  • 我在之前的博客里面已經實現過我們所熟悉的冒泡排序代碼算法:
#include<stdio.h>
void input(int* arr, int sz)//輸入待排序序列
{for (int i = 0; i < sz; i++){scanf("%d", arr + i);}
}void bubble_sort(int* arr, int sz)//冒泡排序算法
{for (int i = 0; i < sz-1; i++)//sz-1趟比較{int change = 1;//小優化節省時間for (int 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;change = 0;}}if (change == 1)//說明已經有序 {break;}}
}
void print(int* arr, int sz)
{for (int i = 0; i < sz; i++){printf("%d ", *(arr + i));}printf("\n");
}
int main()
{int arr[10] = { 0};int sz = sizeof(arr) /sizeof(arr[0]);input(arr, sz);bubble_sort(arr, sz);print(arr, sz);return 0;
}

但是在這里我們為了響應qsort算法,我們應該根據qort函數中的參數來重新改編冒泡排序。
前面已經提到過qsort函數的函數聲明:
void qsort (void* base, size_t num, size_t size, int (compar)(const void,const void*));
這里我們用void指針來接受待排序序列,是一種泛式的編程,這里就可以接受任何數據類型的排序,這便是void指針的最大優勢。

我們用冒泡排序模擬實現qsort函數的代碼如下(這里我們以排序整型數據為例):

#include<stdio.h>
int int_cmp(const void*p1,const void * p2)//定義比較函數
{ return (*( int *)p1 - *(int *) p2);
}
void _swap(void*p1,void*p2,int size)
{
for(int i=0;i<size;i++)
{
//每一位字節都進行交換,從而做到整個數據類型進行交換。char tmp = *((char *)p1 + i);//我們轉換成char*類型可以理解為轉換成單位字節,然后乘上數據類型字節大小就可以表示任意數據類型*(( char *)p1 + i) = *((char *) p2 + i);*(( char *)p2 + i) = tmp;
}}
void bubble(void*base,int count ,int size,int(*cmp)(void*,void*))
//這里完全模仿qsort函數來定義的
{
for(int i=0;i<count-1;i++)
{
for(int j=0;j<count-1-i)
{
if(cmp((char*)base+j*size,(char*)base+(j+1)*size)>0)//我們轉換成char*類型可以理解為轉換成單位字節,然后乘上數據類型字節大小就可以表示任意數據類型
{_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
}
}
}
}
int main(){int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;bubble(arr, sizeof(arr) /sizeof(arr[0]), sizeof (int), int_cmp);for (i = 0;i<sizeof(arr)/sizeof(arr[0]); i++){printf( "%d ", arr[i]);}printf("\n");return 0;}

總結

本文主要介紹了一個嶄新的概念回調函數,并分析了qsort函數的使用,以及用冒泡排序來模擬qsort函數,如有錯誤,請批評指正,感謝支持

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

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

相關文章

持續總結中!2024年面試必問 20 道 Redis面試題(四)

上一篇地址&#xff1a;持續總結中&#xff01;2024年面試必問 20 道 Redis面試題&#xff08;三&#xff09;-CSDN博客 七、Redis過期鍵的刪除策略&#xff1f; Redis 過期鍵的刪除策略主要涉及以下幾種方式&#xff1a; 1. 定時刪除&#xff08;Timed Expiration&#xff…

面試的內容

1.C的三大特性&#xff1a;封裝&#xff0c;繼承&#xff0c;多態 2.C11的特性 3.NULL與Nullptr的區別: nullptr是一個特殊的空指針常量&#xff0c;不能被隱式轉換為其他類型。 NULL 在一些情況下可能會發生隱式類型轉換 4.智能指針 5.stl/Qt stl stl容器包含哪些&…

如何在沒有密碼或Face ID的情況下解鎖iPhone

iPhone 是一款以其一流的安全功能而聞名的設備&#xff0c;包括面容 ID 和密碼。但是&#xff0c;你有沒有想過&#xff0c;如果沒有這些安全措施&#xff0c;你是否可以解鎖iPhone&#xff1f;無論您是忘記了密碼&#xff0c;Face ID不起作用&#xff0c;還是只是對其他方法感…

5.23-

回顧 I0多路復用的原理? 程序首先向操作系統發起一個IO多路復用請求&#xff0c;告訴操作系統需要監視哪些IO通道。這些IO通道可以包括網絡套接字、文件描述符等操作系統隨后會將這些IO通道放入一個隊列中&#xff0c;并在某個IO通道就緒時&#xff08;如數據到達、文件可讀…

「YashanDB遷移體驗官」Mysql生產環境遷移至YashanDB數據庫深度體驗

「YashanDB遷移體驗官」Mysql生產環境遷移至YashanDB數據庫深度體驗 1. 前言1.1 產品介紹1.2 產品架構1.3 產品規格1.3.1 數據庫版本支持1.3.2 數據類型支持 2. YMP安裝2.1 環境說明2.2 執行安裝2.3 訪問YMP2.3.1 YMP登錄界面2.3.2 YMP遷移流程 3. YMP數據遷移3.1 創建數據源3.…

離線模式下載安裝gcc-4.8.5

目錄 一,下載gcc離線安裝包 二,下載gcc依賴包 三,安裝gcc 1, 解壓 2, 將依賴庫放置環境 3, 安裝 3.1自動安裝 3.1.1 執行依賴庫的編譯 3.1.2 新建編譯目錄 3.1.3 配置編譯環境 3.1.4 編譯 3.1.5 安裝 3.2 手動安裝 3.2.1 安裝GMP-4.3.2 3.2.2 安裝mpf…

【pytorch】 Win11下cuda,cudnn以及pytorch環境安裝

Win11下cuda&#xff0c;cudnn以及pytorch環境安裝 CUDA環境安裝1. 查看CUDA版本1.1 打開NVIDIA控制面板&#xff0c;可以點右下角的NVIDIA設置1.2 點擊系統信息&#xff0c;選擇組件查看CUDA版本 2. 下載對應的CUDA版本3. 安裝3.1 啟動exe文件安裝 4. 驗證安裝結果 CUDNN 環境…

【MySQL精通之路】InnoDB-啟動選項和系統變量

系統變量可以在服務器啟動時設置TRUE或FALSE啟用禁用&#xff0c;也可以通過使用--skip前綴來禁用 例如&#xff1a; 要啟用或禁用InnoDB自適應哈希索引&#xff0c;可以在命令行中使用--skip-innodb-adaptive-hash-index或--innodb-adaptive-hash-index&#xff0c;或者在配置…

JavaSE : 注解 Annotation

注解 Java中的注解&#xff08;Annotation&#xff09;是一種元數據形式&#xff0c;用于向編譯器或JVM提供有關程序元素&#xff08;如類、方法、變量、參數和包&#xff09;的附加信息。注解不會直接影響程序的行為或結構&#xff0c;但它們可以被編譯器、開發工具或運行時環…

Apache CXF Aegis databinding SSRF 高危漏洞修復

一、漏洞修復 Apache CXF Aegis databinding SSRF漏洞 Spring Web UriComponentsBuilder URL解析不當漏洞 二、修復步驟 1、Apache CXF Aegis databinding SSRF漏洞修復 步驟&#xff1a; 進入服務器搜索 databinding find -name *databinding* 發現版本是3.1.6 果斷…

數據結構~~帶環鏈表的環開始的節點位置**兩種方法

1.帶環鏈表環開始的位置 &#xff08;1&#xff09;上面的這個測試用例使用的是包含了4個節點的帶環鏈表&#xff0c;我們要找的就是鏈表里面的環開始的節點的位置&#xff0c;拿這個測試用例而言&#xff0c;就是2這個節點&#xff0c;從這個節點開始&#xff0c;我們的鏈表就…

【第16章】MyBatis-Spring之SqlSession

文章目錄 前言一、SqlSessionTemplate1. 創建2. 使用3. 批量操作3.1 創建SqlSessionTemplate3.2 service3.3 controller 二、SqlSessionDaoSupport總結 前言 在 MyBatis 中&#xff0c;你可以使用 SqlSessionFactory 來創建 SqlSession。 一旦你獲得一個 session 之后&#xf…

源碼部署EFK

目錄 資源列表 基礎環境 關閉防護墻 關閉內核安全機制 修改主機名 添加hosts映射 一、部署elasticsearch 修改limit限制 部署elasticsearch 修改配置文件 單節點 集群(3臺節點集群為例) 啟動 二、部署filebeat 部署filebeat 添加配置文件 啟動 三、部署kiban…

單點登錄【demo】

前言 2023-07-29 15:44:56 公開發布于 2024-5-22 00:04:56 單點登錄【demo】 以下是 Java 實現單點登錄的示例代碼&#xff1a; 單點登錄&#xff08;Single Sign-On&#xff0c;SSO&#xff09;是一種身份認證和授權機制&#xff0c;可以使用戶在多個應用程序或系統之間使…

SQL常用基礎語句(一)-- FGHIJ開頭

GROUP BY GROUP BY語法可以根據給定數據列的每個成員對查詢結果進行分組統計&#xff0c;最終得到一個分組匯總表。在GROUP BY子句后面包含了一個HAVING子句&#xff0c;HAVING類似于WHERE&#xff0c;&#xff08;唯一的差別是WHERE過濾行&#xff0c;HAVING過濾組&#xff0…

【C/C++筆試練習】TCP、IP廣播、ARP協議、IP路由器、MAC協議、三次握手、TCP/IP、子網劃分年、會抽獎、抄送列表

文章目錄 C/C筆試練習選擇部分&#xff08;1&#xff09;TCP&#xff08;2&#xff09;IP廣播&#xff08;3&#xff09;ARP協議&#xff08;4&#xff09;IP路由器&#xff08;5&#xff09;MAC協議&#xff08;6&#xff09;三次握手&#xff08;7&#xff09;TCP/IP&#xf…

PHP在線制作表白網源碼

PHP在線制作表白網源碼&#xff0c;送女友個驚喜吧&#xff0c;無數據庫&#xff0c;上傳就能用&#xff0c;后臺/admin&#xff0c;賬號密碼都是admin 百度網盤&#xff1a;https://pan.baidu.com/s/1rbD2_8IsP9UPLK-cdgEXfA?pwdre59

AWS安全性身份和合規性之Secrets Manager

AWS Secrets Manager是一項AWS托管的服務&#xff0c;用于安全地存儲、管理和輪轉敏感信息&#xff0c;如數據庫密碼、API密鑰、OAuth令牌等。AWS Secrets Manager助您在整個生命周期內輕松管理、檢索和輪換數據庫憑證、API密鑰和其他密鑰。 關鍵詞&#xff1a;集中管理、加密…

sql使用加和進行合并去重并提升速率

背景 有三張表ltd1 、ltd0051和、ltd0011ltd1作為主表&#xff0c;左關聯 ltd0051和ltd0011如果ltd0051有兩條重復數據、td0011有兩條重復數據&#xff0c;左關聯之后就會得到4條&#xff0c;同時ltd0051和ltd0011這兩條數據都是正確&#xff0c;基于主鍵我們需要將兩個相同主鍵…

【全開源】AJAX家政上門服務系統小程序自營+多商家(高級授權)+獨立端

基于FastAdmin和原生微信小程序開發的一款同城預約、上門服務、到店核銷家政系統&#xff0c;用戶端、服務端(高級授權)、門店端(高級授權)各端相互依賴又相互獨立&#xff0c;支持選擇項目、選擇服務人員、選擇門店多種下單方式&#xff0c;支持上門服務和到店核銷兩種服務方式…