【c語言】指針進階(超詳細)

在這里插入圖片描述

文章目錄

  • ? 指向函數指針數組的指針
    • 📌指向函數指針數組的指針的定義
    • 📌指向函數指針數組的數組指針的使用
  • ?回調函數
    • 📌 回調函數的定義
    • 📌 回調函數的使用
  • ?qsort函數
    • 📌 qsort函數的作用
    • 📌qsort函數的定義
    • 📌qsort函數的頭文件
    • 📌1.qsort函數排序整型數組
      • 比較函數compare
      • 主函數
    • 📌2.qsort函數排列結構體數據
      • 【1】排列結構體中的整型類型
        • 比較函數
        • 主函數以及結構體
      • 【2】排列結構體中的字符串類型
        • 比較函數compare
        • 主函數
      • 【3】 排列結構體中的浮點型數據
        • 比較函數compare
        • 主函數
    • 📌3.qsort函數排列字符數組類型數據
        • 比較函數compare
        • 主函數
  • ?使用回調函數,模擬實現qsort(采用冒泡的方式)
    • 冒泡排序(傳送門在此[冒泡排序](https://blog.csdn.net/yyqzjw/article/details/131909947?spm=1001.2014.3001.5501))
    • 模擬實現qsort排整型數組(利用冒泡排序方式)
  • 總結


? 指向函數指針數組的指針

指向函數指針數組的指針是一個 指針, 指針指向一個 數組 ,數組的元素都是 函數指針 ;
如何定義?

📌指向函數指針數組的指針的定義

我們可以先定義五個函數

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;
}

分別計算兩個變量的加減乘除,然后定義一個函數指針數組用來存放上面4個函數的地址

int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; 

空出來一個是為了讓加減乘除函數與下標對應上

int (*(*pp)[5])(int x,int y) = &p;

這個是指向函數指針數組的數組指針,ta指向的函數指針數組的類型是去掉*pp

int (*()[5])(int x,int y)

這個類型說明數組指針指向的是數組長度是5,每個元素都是地址,每個元素的類型是

int ()(int x,int y)

畫圖說明一下關系
在這里插入圖片描述

📌指向函數指針數組的數組指針的使用

怎么通過這個數組指針pp去使用加減乘除這幾個函數呢???代碼如下

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(*p[5])(int x, int y) = { 0, add, sub, mul, div };int (*(*pp)[5])(int x, int y) = &p;int ret = (*(*pp + 1))(3, 4);printf("%d", ret);
}

在這里插入圖片描述
在這塊解釋一下子

 (*(*pp + 1))(3, 4)

pp存放的是p[]整個數組的地址,*pp相當于拿到這個數組,也相當于拿到數組的數組名,相當于數組首元素地址,*pp+1為第二個元素地址, 如果在對其解引用得到的是 *(pp+1),這個就是數組第二個元素的內容,也就是說add的地址,因為&add和add打印的結果一樣,使用該函數計算3+4;
add(3,4)等價 (
(*pp + 1))(3, 4)

?回調函數

📌 回調函數的定義

回調函數pp()就是一個通過函數指針void(*p)()調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數print(&pp);,當這個指針被用來調用其所指向的函數時pp(),我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在if (1)特定的事件或條件發生時由另外的一方調用的,用于對該事件或
條件進行響應。

📌 回調函數的使用

void pp()
{printf("hahahahaha\n");
}
void print(void(*p)())
{if (1){p();}
}
int main()
{  print(&pp);return 0;
}

根據回調函數的定義,可知pp()函數就是回調函數。

?qsort函數

📌 qsort函數的作用

我們之前學過的冒泡排序,可以將一個整型數組排好序,如果讓我們去排序浮點型,字符型,結構體型,我們應該怎么辦呢
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
qsort函數可以解決這個問題,萬物皆可排。那誰誰你怎么插隊呢快去排隊

📌qsort函數的定義

在這里插入圖片描述
在msdn上找到定義,我們可以將定義復制過來

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

在這里插入圖片描述
什么???英格力士

在這里插入圖片描述
下面有請過英語四級的小張同學給翻譯翻譯哈哈哈哈哈
咳咳,我來了

🚫1.base指針指向的是要排序數組的首地址,因為不清楚要排序的是什么類型的數據,這里用void 可以接收任何類型的指針
2.num是該數組的元素個數
3.width是每個元素的字節大小
4.自定義比較函數compare
第四個位置是一個函數指針來接收一個比較函數的地址,參數用兩個指針接收要比較兩個元素的地址,因為不知道比較什么類型的數據,就用void
的指針接收,比較函數是根據你自己要排列的數據類型自己定義的。在這里插入圖片描述
第一個元素大于第二個元素返回大于0的,小于第二個元素返回小于0,等于返回0

📌qsort函數的頭文件

在這里插入圖片描述

📌1.qsort函數排序整型數組

比較函數compare

int int_cmp(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}

這里要排整型數據,將void的指針強制類型轉化為int,一次可以訪問4個字節 ,解引用也可以得到一個整型數據

主函數


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 (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}printf("\n");return 0;
}

在這里插入圖片描述

注意:如果要排成降序只需要將比較函數中的
int int_cmp(const void* p1, const void* p2)
{
return ((int)p2 - (int)p1);
}

📌2.qsort函數排列結構體數據

【1】排列結構體中的整型類型

比較函數

int int_cmp_age(const void* p1, const void* p2)//按年齡比較
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}

主函數以及結構體

struct Stu {char name[20];int age;float score;
};
int main()
{int i = 0;struct Stu arr[3] = {{"zhangjiawang",18,100},{"zhumiao",50,76},{"liuliu",19,76}};int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr,sz, sizeof(arr[0]), int_cmp_age);for (int i = 0; i < 3; i++){printf("%d ", arr[i].age);}return 0;
}

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

【2】排列結構體中的字符串類型

比較函數compare

int int_cmp_age(const void* p1, const void* p2)
{return (strcmp(((struct Stu*)p1)->name), ((struct Stu*)p2)->name);
}

字符串比較用strcmp函數,記得添加頭文件string.h在這里插入圖片描述
這個是strcmp函數的返回值,和比較函數剛好對應上

主函數

int main()
{int i = 0;struct Stu arr[3] = {{"zhangjiawang",18,100},{"zhumiao",50,76},{"liuliu",19,76}};int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr,sz, sizeof(arr[0]), int_cmp_age);for (int i = 0; i < 3; i++){printf("%s ", arr[i].name);}return 0;
}

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

【3】 排列結構體中的浮點型數據

比較函數compare

int int_cmp_float(const void* p1, const void* p2)
{return (*(float*)p1 -*(float*)p2);
}

主函數

int main()
{int i = 0;struct Stu arr[3] = {{"zhangjiawang",18,100.0},{"zhumiao",50,76.0},{"liuliu",19,77.0}};int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr,sz, sizeof(arr[0]), int_cmp_float);for (int i = 0; i < 3; i++){printf("%.1f ", arr[i].score);}return 0;
}

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

📌3.qsort函數排列字符數組類型數據

比較函數compare

int int_cmp(const void* p1, const void* p2)
{return (*(char*)p1 - *(char*)p2);
}

主函數

int main()
{char arr[] = {'b','c','a','f','z','q'};int i = 0;qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(char), int_cmp);for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%c ", arr[i]);}printf("\n");return 0;
}

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

?使用回調函數,模擬實現qsort(采用冒泡的方式)

冒泡排序(傳送門在此冒泡排序)

int main()
{int  arr[10] = {10,9,8,7,6,5,4,3,2,1};int sz = sizeof(arr) / sizeof(arr[0]);//數組長度int i;for (i = 0; i < sz - 1; i++)//趟數{int j = 0;for (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;}}}for (i = 0; i < sz; i++)//打印數組{printf("%d ", arr[i]);}
}

模擬實現qsort排整型數組(利用冒泡排序方式)

主函數

int main()
{int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;bubble_qsort(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函數

在這里插入圖片描述
將冒泡排序的實現copy過來,然后對其修改
在這里插入圖片描述
大概替換的思想是
`void bubble_sort(void* base, int num, int width, int cmp)
{int i;
for (i = 0; i < num - 1; i++)
{
int j = 0;
for (j = 0; j < num - 1 - i; j++)
{
if (比較函數的返回值)
{交換數組元素的函數}
}
}
}

比較函數compare(這里因為要排的是整型數組,用上面整型數組的比較函數就行)

int cmp(const void* p1, const void* p2)
{
return ((int)p1 - (int)p2);
}

現在的問題是怎么使用bubble_sort函數的參數找到要比較兩個元素的地址??
在這里插入圖片描述

代碼:cmp((char*)base+j*width),(char*)base+(j+1)*width)>0
問題1:為什么要將base地址存在char的指針中?
方便訪問任意類型數據,因為width是一個元素的字節大小,如果訪問一個整型的地址,width就是4,char
的指針+4剛好跳過4個字節,如果把base地址用int*接收,如果width還是4的話,+4就跳過的不是一個元素,而是4個元素,相鄰的元素根本就比不了大小。
如果j=0的話,上面傳到比較函數中的就是第一個元素地址和第二個元素地址
替換后的bubble_sort

void bubble_sort(void* base, int num, int width, int cmp)
{int i;for (i = 0; i < num - 1; i++){int j = 0;for (j = 0; j < num - 1 - i; j++){if (cmp((char*)base + j * width), (char*)base + (j + 1) * width) > 0){交換數組元素的函數}}}}
}
現在的問題是怎么交換兩個地址上的元素。交換數組元素的函數👇
void swap(char* p1, char* p2, int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}

這里的交換函數是按字節交換的,比如說數組前兩個元素1,3,假如說內存為小端存儲,實現前兩個元素交換在這里插入圖片描述
替換后的bubble_sort

void bubble_sort(void* base, int num, int width, int cmp)
{int i;for (i = 0; i < num - 1; i++){int j = 0;for (j = 0; j < num - 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));}}}}
}

整體代碼展示

`#include <stdio.h>
int cmp(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}
void swap(char* p1, char* p2, int width)
{int i = 0;for (i = 0; i < width; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}
void bubble_sort(void* base, int num, int width, int cmp(const void* p1, const void* p2))
{int i;for (i = 0; i < num - 1; i++){int j = 0;for (j = 0; j < num - 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);}}}}
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), cmp);for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}printf("\n");return 0;
}`

總結

希望這篇文章可以給你帶來幫助,如果有不對的地方,或者有哪里不理解的地方,請私信我,謝謝大家支持,下篇見,以上

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

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

相關文章

【佳佳怪文獻分享】安全人機交互的學習責任分配與自動駕駛應用

標題&#xff1a;Learning Responsibility Allocations for Safe Human-Robot Interaction with Applications to Autonomous Driving 作者&#xff1a;Ryan K. Cosner, Yuxiao Chen, Karen Leung, and Marco Pavone 來源&#xff1a;2023 IEEE International Conference on …

1.1 : DNA 螺旋

概述 脫氧核糖核酸(DNA)是負責在所有生物體和大多數病毒中代代相傳性狀的遺傳物質。DNA由兩條相互纏繞形成雙螺旋的核苷酸鏈組成。DNA 結構的發現是在近一個世紀的時間里逐步發現的,代表了科學史上最著名、最迷人的故事之一。 DNA 結構詳細信息 每條 DNA 鏈均由稱為核苷酸…

安全防御問題

SSL VPN的實現&#xff0c;防火墻需要放行哪些流量&#xff1f; 實現 SSL VPN 時&#xff0c;在防火墻上需要放行以下流量&#xff0c; SSL/TLS 流量&#xff1a;SSL VPN 通過加密通信來確保安全性&#xff0c;因此防火墻需要允許 SSL/TLS 流量通過。一般情況下&#xff0c;SSL…

lua實現http的異步回調

想用lua實現與http服務器的通信&#xff0c;請求一些數據會回來&#xff0c;默認lua.socket.http是同步的&#xff0c;所以想弄一個異步的方式 測試環境 lua 5.1 同步 以下是同步的代碼&#xff0c;其中http.request會被阻塞住的 local function send_request()local res,…

【QT】 Word模板編輯、轉PDF格式

很高興在雪易的CSDN遇見你 ,給你糖糖 歡迎大家加入雪易社區-CSDN社區云 前言 本文分享基于QT進行Word模板編輯以及Word轉PDF的技術,希望對各位小伙伴有所幫助! 感謝各位小伙伴的點贊+關注,小易會繼續努力分享,一起進步! 你的點贊就是我的動力(^U^)ノ~YO 目錄 …

機器學習-特征選擇:如何使用遞歸特征消除算法自動篩選出最優特征?

一、引言 在實際應用中&#xff0c;特征選擇作為機器學習和數據挖掘領域的重要環節&#xff0c;對于提高模型性能和減少計算開銷具有關鍵影響。特征選擇是從原始特征集中選擇最相關和最具區分力的特征子集&#xff0c;以提高模型的泛化能力和可解釋性。 特征選擇在實踐中具有以…

計算機競賽 python opencv 深度學習 指紋識別算法實現

1 前言 &#x1f525; 優質競賽項目系列&#xff0c;今天要分享的是 &#x1f6a9; python opencv 深度學習 指紋識別算法實現 &#x1f947;學長這里給一個題目綜合評分(每項滿分5分) 難度系數&#xff1a;3分工作量&#xff1a;4分創新點&#xff1a;4分 該項目較為新穎…

什么是Java中的觀察者模式?

Java中的觀察者模式是一種設計模式&#xff0c;它允許一個對象在狀態發生改變時通知它的所有觀察者。這種模式在許多情況下都非常有用&#xff0c;例如在用戶界面中&#xff0c;當用戶與界面交互時&#xff0c;可能需要通知其他對象。 下面是一個簡單的Java代碼示例&#xff0…

代碼質量檢查工具SonarQube

Devops流水線之SonarQube 文章目錄 Devops流水線之SonarQube1. 軟件功能介紹及用途2. 軟件環境搭建與使用2.1 使用方法2.2 SonarQube相關屬性說明2.3 Sonar配置文件內容說明 3. 使用環節4. 檢查方法 1. 軟件功能介紹及用途 SonarQube是一個用于代碼質量管理的開源平臺&#xf…

element-ui table表格,根據縮放自適應

安裝依賴 npm install af-table-columnmain.js 中引入依賴&#xff0c; import Vue from vue import ElementUI from element-ui //需要按需引入&#xff0c;先引入vue并引入element-ui import AFTableColumn from af-table-column Vue.use(AFTableColumn)demo樣式&#xff1…

Python Opencv實踐 - 圖像放射變換

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) rows,cols img.shape[:2] print(img.shape[:2])#使用getAffineTransform來獲得仿射變換的矩陣M #cv.getAffineTransform(…

Java:正則表達式案例:爬數據,重復數據替換,數據分割

使用正則表達式查找一段文本中的內容 需求:請把下面文本中的電話&#xff0c;郵箱&#xff0c;座機號碼&#xff0c;熱線都爬取出來。 String data "電話:1866668888&#xff0c;18699997777\n" "或者聯系郵箱: boniuitcast.cn&#xff0c;\n" "座機…

Loop Transformations

Java的Loop Transformations 很多人都知道,在編程中,循環結構是十分重要的。但是,在不同的情況下,相同的循環結構可能會導致不同的性能表現。因此,在優化程序的性能時,循環結構的優化是一個不可忽略的方面。這就是Loop Transformations(循環轉換)的作用。本文將以詳細…

16 dplsys GAN

和有監督的分類工作不同&#xff0c;生成任務的目標更不明確。難以評價生成結果的好壞。 Oracle discriminator 假設我們有一個先知判別器oracle discriminator可以分辨我們生成的內容是真還是假。 我們想讓生產成的結果足夠真實&#xff0c;所以要 fool Oracle discriminato…

Android布局【GridLayout】

文章目錄 GridLayout概述常見屬性子控件屬性項目結構主要代碼 GridLayout概述 GridLayout也名網格布局,該布局與TableLayout類似&#xff0c;但與其相比&#xff0c;GridLayout會更加的靈活&#xff0c;比如 TableLayout不能將兩行進行一個合并&#xff0c;只能將兩列進行一個…

Declare 關鍵字在 TypeScript 中如何正確使用?

如果您編寫 TypeScript 代碼的時間足夠長,您就已經看到過declare關鍵字。但它有什么作用,為什么要使用它? declare關鍵字告訴 TypeScript 編譯器存在一個對象并且可以在代碼中使用。 本文解釋了聲明關鍵字并通過代碼示例展示了不同的用例。 定義 在 TypeScript 中,decl…

【MySQL--->數據類型】

文章目錄 [TOC](文章目錄) 一、數據類型分類二、整型類型三、bit(位)類型四、float類型五、decimal類型六、char和varchar類型1.char類型2.varchar3.char與varchar的區別 七、日期與時間類型八、enum和set 一、數據類型分類 二、整型類型 數值類型有數據存儲上限,而且每個類型都…

4、Rocketmq之存儲原理

CommitLog ~ MappedFileQueue ~ MappedFile集合 正常情況下&#xff0c;RocketMQ支持消息體字節數最多為1個G。注意該消息體并不單單是消息體body。如果生產的消息其字節數超過1個G則該消息是無法被落盤處理的。因為沒有一個MapperFile文件可以承載該消息所有的字節數。 1.All…

【力扣每日一題】617. 合并二叉樹 dfs bfs 8.14打卡

文章目錄 題目思路代碼 題目 617. 合并二叉樹 難度&#xff1a; 簡單 描述&#xff1a; 給你兩棵二叉樹&#xff1a; root1 和 root2 。 想象一下&#xff0c;當你將其中一棵覆蓋到另一棵之上時&#xff0c;兩棵樹上的一些節點將會重疊&#xff08;而另一些不會&#xff0…

阿里云ACP知識點

前言&#xff1a;記錄ACP錯題 1、在創建阿里云ECS時&#xff0c;每臺服務器必須要包含_______用來存儲操作系統和核心配置。 系統盤&#xff08;不是實例&#xff0c;實例是一個虛擬的計算環境&#xff0c;由CPU、內存、系統盤和運行的操作系統組成&#xff1b;ESC實例作為云…