網(wǎng)站首頁 編程語言 正文
回調(diào)函數(shù)
通過函數(shù)指針調(diào)用的函數(shù),如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個函數(shù),當(dāng)這個指針被用來調(diào)用其所指向的函數(shù)時,我們就說這是回調(diào)函數(shù)。
回調(diào)函數(shù)不是由該函數(shù)的實現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時由另外的一方調(diào)用的,用于對該事件或條件進(jìn)行響應(yīng)。
舉例:
#include<stdio.h>
void menu()
{
printf("********************************\n");
printf("** 1.and 2.sub **\n");
printf("** 3.mul 4.div **\n");
printf("********************************\n");
}
int add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int sub(int x, int y)
{
int z = 0;
z = x - y;
return z;
}
int mul(int x, int y)
{
int z = 0;
z = x * y;
return z;
}
int div(int x, int y)
{
int z = 0;
z = x / y;
return z;
}
void Calc(int(*pf)(int, int))//int(*pf)(int, int)等于add,只不過選用不同的方式進(jìn)行調(diào)用
{
int x = 0;
int y = 0;
printf("請輸入兩個操作數(shù):>");
scanf_s("%d%d", &x, &y);
printf("%d\n", pf(x, y));//通過指針對add函數(shù)進(jìn)行調(diào)用,而不是像之前那樣使用函數(shù)名進(jìn)行調(diào)用
}
int main()
{
int input = 0;
do
{
menu();
printf("請選擇:>");
scanf_s("%d", &input);
switch (input)
{
case 1:
Calc(add);//將add函數(shù)的地址傳遞過去,這里的add函數(shù)為回調(diào)函數(shù)
break;
case 2:
Calc(sub);
case 3:
Calc(mul);
case 4:
Calc(div);
}
} while (input);
}
指向函數(shù)指針數(shù)組的指針
指向函數(shù)指針數(shù)組的指針是一個指針,指向一個數(shù)組,數(shù)組的元素都是函數(shù)指針;
如何定義?
int arr[10] = { 0 };//整型數(shù)組
int(*p)[10] = &arr;//取出數(shù)組的地址
int (*pf)(int, int);//函數(shù)指針
int(*pfarr[4])(int, int);//pfarr是一個數(shù)組,函數(shù)指針的數(shù)組
int(*(*ppfarr)[4])(int, int) = &pfarr;//ppfarr指向函數(shù)指針數(shù)組的指針
//pfarr是一個數(shù)組指針,指針指向的數(shù)組有4個元素
//指向的數(shù)組的每個元素的類型是函數(shù)指針int(*)(int,int)
void*
可以用來接收任何類型數(shù)據(jù)的地址,別名萬能指針
既然可以存放任何類型的地址,那么是不是也可以解引用訪問存放的值?
下面我們通過示例:
#include<stdio.h>
int main()
{
int a = 10;
void* p = &a;
printf("%d\n", *p);
}
通過輸出結(jié)果我們發(fā)現(xiàn),程序并沒有被正確的運行,而是告訴我們,我們進(jìn)行了非法間接尋址。
那么為什么會出現(xiàn)這樣的現(xiàn)象呢?
原因是void*雖然可以接受任意類型的地址,但它自己本身的類型是空類型,那么在解引用操作的時候,系統(tǒng)并不知道它的類型,因此不知道需要分配給其幾個字節(jié),指針類型決定了它的字節(jié)大小。
因此,void*不能進(jìn)行解引用操作
那么可以進(jìn)行++/–操作嗎?
我們通過實例進(jìn)行驗證一下:
#include<stdio.h>
int main()
{
int a = 10;
void* p = &a;
p++;
}
程序依然沒有正確運行,編譯器指出了錯誤的原因:void未知的大小,和上面一樣的道理,我們并不清楚此時存放在void里面的數(shù)據(jù)是什么類型,自然也不知道它所占據(jù)內(nèi)存空間的大小,因此步長是無法確定的。
因此void*也不可以進(jìn)行++/–操作
qsort(qulick sort)-庫函數(shù)
適用的場景:適用于對某一組數(shù)據(jù)進(jìn)行快速排序
qsort(s,sz,sizeof(s[0]),cmp_stu_by_name);
//第一個參數(shù)s:待排序數(shù)組的首元素地址
//第二個參數(shù)sz:待排序數(shù)組的元素個數(shù)
//第三個參數(shù)sizeof(s[0]):待排序數(shù)組的每個元素的大小,單位是字節(jié)
//第四個參數(shù):是函數(shù)指針,比較兩個元素的所用函數(shù)的地址
//這個函數(shù)使用者自己實現(xiàn)函數(shù)指針的兩個參數(shù)是:待比較的兩個元素的地址
舉例:
常規(guī)方法:冒泡排序:
#include<stdio.h>
int main()
{
int arr[10] = { 9,2,3,1,4,5,7,6,0,91 };
int i, j=0,temp;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz-1; i++)//決定需要比較多少次
{
for (j=0; j < sz-1-i; j++)
{
if (arr[j+1] > arr[j])
{
temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
輸出:
91 9 7 6 5 4 3 2 1 0
但是,這種方法的局限性非常大,執(zhí)行效率也不高。
因此在進(jìn)行數(shù)據(jù)類型的排序問題時,我們可以選擇qsort函數(shù):
下面我們就來學(xué)習(xí)qsort函數(shù):
//int (*cmp)(const void *,const void *);
qsort(*s, n, sizeof(s[0]), cmp);
其中第一個參數(shù)s是一個地址,即參與排序的首地址; n是需要排序的數(shù)量; sizeof(s[0])則是每一個元素占用的空間大小;
指向函數(shù)的指針,用于確定排序的順序。
sz=sizeof(arr)/sizeof(arr[0])
qsort(a, sz,arr[0], cmp);
//其中cmp函數(shù)應(yīng)寫為:
int cmp(const void *a, const void *b)//void*可接受任意類型的數(shù)據(jù)
{
return *(int*)a - *(int*)b; //由小到大排序
//return *(int *)b - *(int *)a; 由大到小排序
}
對于整形數(shù)據(jù)的比較實現(xiàn)過程:
#include<stdio.h>
#include<stdlib.h>#qsort的頭文件
int cmp_int(const void* e1, const void* e2)//e1和e2是用來接收要比較的兩個元素的地址
{
return *(int*)e1 - *(int*)e2;
}
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr,sz, sizeof(arr[0]), cmp_int);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);//整形打印%d
}
return 0;
}
輸出:
0 1 2 3 4 5 6 7 8 9
對于浮點型數(shù)據(jù)的比較實現(xiàn)過程:
int cmp_float(const void* e1, const void* e2)//e1和e2是用來接收要比較的兩個元素的地址
//由于cmp_float函數(shù)的返回類型是int,因此,需要進(jìn)行轉(zhuǎn)化
{
//可用if分支語句
/*if (*(float*)e1 == *(float*)e2)
return 0;
else if (*(float*)e1 > *(float*)e2)
return 1;
else
return 0;*/
//也可用return直接返回
return ((int) * (float*)e1 - *(float*)e2);
}
int main()
{
float f[] = { 9.0,8.0,7.0,6.0,5.0,4.0 };
int sz = sizeof(f) / sizeof(f[0]);
qsort(f,sz, sizeof(f[0]), cmp_float);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%f ", f[i]);//浮點型打印%f
}
return 0;
}
對于結(jié)構(gòu)體類型數(shù)據(jù)的實現(xiàn)過程:
結(jié)構(gòu)體類型和整形浮點型在排序的時候有略微區(qū)別,結(jié)構(gòu)體類型并不能直接進(jìn)行比較,而要按照某一變量,例如:名字,年齡等等
按照年齡比較:
#include<stdio.h>
#include<stdlib.h>
struct stu//定義一個結(jié)構(gòu)體
{
char name[20];
int age;
};
int cmp_s_stu(const void* e1, const void* e2)//e1和e2是用來接收要比較的兩個元素的地址
{
return ((struct stu*)e1)->age - ((struct stu*)e2)->age;//告訴編譯器你想用什么樣的方式進(jìn)行排序
}
int main()
{
struct stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s,sz, sizeof(s[0]), cmp_s_stu);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s ", s[i]);#字符串型,以%s進(jìn)行打印
}
return 0;
}
按照名字進(jìn)行比較:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stu
{
char name[20];
int age;
};
int cmp_s_stu(const void* e1, const void* e2)//e1和e2是用來接收要比較的兩個元素的地址
{
return strcmp(((struct stu*)e1)->name,((struct stu*)e2)->name);//比較名字就是比較字符串
//注意字符串在比較大小的時候,不能直接用加減法進(jìn)行比較,而要用strcmp()函數(shù)
}
int main()
{
struct stu s[3] = { {"zhangsan",20},{"lisi",30},{"wangwu",10} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s,sz, sizeof(s[0]), cmp_s_stu);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%s ", s[i]);#字符串型,以%s進(jìn)行打印
}
return 0;
}
原文鏈接:https://blog.csdn.net/m0_64365419/article/details/125791909
相關(guān)推薦
- 2022-04-27 Python中python-nmap模塊的使用介紹_python
- 2022-06-25 Python+PuLP實現(xiàn)線性規(guī)劃的求解_python
- 2022-11-24 React?hooks使用方法全面匯總_React
- 2022-11-22 Android本地存儲方法淺析介紹_Android
- 2022-09-20 C#?Winform實現(xiàn)圓角無鋸齒按鈕_C#教程
- 2022-01-01 stopPropagation是什么意思以及什么是事件的原理
- 2022-07-30 Linux文件管理命令行
- 2022-07-03 Golang之理解錯誤輸出
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支