博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C语言函数指针和回调函数
阅读量:7021 次
发布时间:2019-06-28

本文共 4874 字,大约阅读时间需要 16 分钟。

 

函数指针

通常我们可以将指针指向某类型的变量,称为类型指针(如,整型指针)。若将一个指针指向函数,则称为函数指针。

函数名的意义

函数名代表函数的入口地址,同样的,我们可以通过根据该地址进行函数调用,而非直接调用函数名。

1 void test001(){ 2     printf("hello, world"); 3 } 4  5 int main(){ 6  7     printf("函数入口地址:%d", test001);//qt中的函数入口地址不会变,C中会变,这里仅为了说明问题 8     //test001(); 9     int *testADD = (int *)20123883;//将地址转化为int型指针10     void(*myfunc)() = testADD;//将函数写成函数指针,有些书上会写&testADD11     myfunc(); //调用函数指针12     system("pause");13     return 0;14 }

另外,还有以下结论:

(1)test001的函数名与myfunc函数指针都是一样的,即都是函数指针。test001函数名是一个函数指针常量,而myfunc是一个函数指针变量,这是它们的关系。

1 int test001(int a, char b){ 2     printf("hello, world\n"); 3     return 0; 4 } 5  6 int main(){ 7  8     int(*myFun)(int, char) = test001; 9     myFun = test001;10 11     //下面四种表达式的结果是相同的12     int a = 10;13     char b = 's';14     myFun(a, b);15     (*myFun)(a, b);16     test001(a, b);17     (*test001)(a, b);18 19     system("pause");20     return 0;21 }

(2)testADD和&testADD的值一样,但表达的含义不同,与数组名和“&数组名”类似,详见。

定义函数指针

定义函数指针最简单的是直接定义函数指针变量,另外还有定义函数类型和定义函数指针类型。

1 int test001(int a, char b){ 2     printf("hello, world\n"); 3     return 0; 4 } 5  6 void test002(){ 7  8     //定义函数类型 9     typedef int(Fun)(int, char);10     Fun *funFir = test001;11 12     //定义函数指针类型13     typedef int(*FunP)(int, char);14     FunP funSec = test001;15 16     //定义函数指针变量17     int(*funThi)(int, char) = NULL;//若报错,在强制转型,(int(*)(int , char))NULL18     funThi = test001;19 }

函数指针用于形参

这种用法通常出现在回调函数中,一般回调函数用于定制操作,下面的例子将说明如何进行定制操作

1 /* 2 ----------------------- 3 函数指针用作另一个函数的参数 4 ----------------------- 5 */ 6 int con1(int a, int b){ 7     return a + b; 8 } 9 10 int con2(int a, int b){11     return a - b;12 }13 14 int con3(int a, int b){15     return a + b + 10;16 }17 18 //在函数体中显式调用函数,将失去灵活性19 //尽管我可以用switch实现三种con的切换20 void doc(){21     int a = 10;22     int b = 20;23     int ret = con1(a, b);24 }25 26 //用如下的调用方式,调用者并不知道调用的哪个函数27 //因此根据函数指针的函数原型可以自己实现新函数,并进行调用28 int doc_p(int(*temp)(int ,char)){29     int a = 10;30     int b = 20;31     int ret = temp(a,b);32     return ret;33 }34 35 /*36 ---------------------37 函数指针数组38 ---------------------39 */40 void func1(){41     printf("a");42 }43 void func2(){44     printf("a");45 }46 void func3(){47     printf("a");48 }49 50 void test003(){51     int(*func[3])();52     func[0] = func1;53     func[1] = func2;54     func[2] = func3;55 56     for (int i = 0; i < 3; ++i)57     {58         func[i];59     }60 }

为什么我们要把函数作为参数来调用呢,直接在函数体里面调用不好吗?

在这个意义上,“把函数做成参数”和“把变量做成参数”目的是一致的,就是以不变应万变。形参是不变的,而实参是可以定制的。唯一不同的是,普通的实参可以由计算机程序自动产生,而函数这种参数计算机程序是无法自己写出来的,因为函数本身就是程序,它必须由人来写。所以对于回调函数这种参数而言,它的“变”在于人有变或者人的需求有变。

回调函数

回调函数和普通函数完成的功能是一样的,但回调函数更灵活,普通函数在函数体中调用,失去了变量的灵活性,有点类似于模板编程。

(1)首先是通过内存偏移,访问数组的各元素地址,两种方法的结果相同

1 void printAll(void *arr, int eleSize, int len){ 2      3     char *start = (char*)arr; //强制转型 4     for (int i = 0; i < len; ++i){ 5         printf("%d\n", start+i*eleSize);//内存偏移 6     } 7 } 8  9 void test004(){10     int arr[5] = {
1,2,3,4,5};11 printAll(arr, sizeof(int), 5);12 printf("-------------------\n");13 for (int i = 0; i < 5; ++i){14 printf("%d\n", &arr[i]);15 }16 }17 18 int main(){19 test004();20 21 system("pause");22 return 0;23 }

(2)对上面的函数用函数指针进行改写

1 //添加函数指针作形参,必须写明变量名 2 void printAll(void *arr, int eleSize, int len, void(*print)(void *data)){ 3      4     char *start = (char*)arr; //强制转型 5     for (int i = 0; i < len; ++i){ 6         char *eleAddr = start + i*eleSize; 7         print(eleAddr); 8         //print(start+i*eleSize); 9     }10 }11 12 //自定义的被调用函数13 void Myprint(void * data){14     int *p = (int *)data;15     printf("%d\n", *p);16 }17 18 void test004(){19     int arr[5] = {
1,2,3,4,5};20 printAll(arr, sizeof(int), 5, Myprint);21 }22 23 int main(){24 test004();25 26 system("pause");27 return 0;28 }

(3)对上面的函数指针添加自定义的数据类型

1 //添加函数指针作形参,必须写明变量名 2 void printAll(void *arr, int eleSize, int len, void(*print)(void *data)){ 3      4     char *start = (char*)arr; //强制转型 5     for (int i = 0; i < len; ++i){ 6         char *eleAddr = start + i*eleSize; 7         print(eleAddr); 8         //print(start+i*eleSize); 9     }10 }11 12 //自定义的被调用函数13 void Myprint(void * data){14     int *p = (int *)data;15     printf("%d\n", *p);16 }17 18 struct Person{19     char name[64];20     int age;21 };22 23 //添加自定义的数据类型打印函数24 void MyprintStruct(void * data){25     struct Person *p = (struct Person  *)data;26     printf("%s,%d\n", p->name, p->age);27 }28 29 30 31 32 void test004(){33     int arr[5] = {
1,2,3,4,5};34 printAll(arr, sizeof(int), 5, Myprint);35 36 struct Person person[] = {37 {
"aaa", 10},38 {
"bbb", 20}39 };40 printAll(person, sizeof(struct Person), 2, MyprintStruct);41 42 }43 44 int main(){45 test004();46 47 system("pause");48 return 0;49 }

回调函数最大的优势在于灵活操作,可以实现用户定制的函数,降低耦合性,实现多样性。

转载于:https://www.cnblogs.com/qinguoyi/p/10198019.html

你可能感兴趣的文章
ubuntu16.04下python2、python3环境选择与python升级(pip版本切换)
查看>>
topcoder srm 435 div1
查看>>
Java读取文本指定的某一行内容的方法
查看>>
leetcode--Best Time to Buy and Sell Stock II
查看>>
Could not load file or assembly 'System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral..
查看>>
php 调用 web Server(短信接口示例)
查看>>
bootstrap-table组合表头
查看>>
蓝桥杯 全球变暖(dfs)
查看>>
[UML]UML系列——类图Class
查看>>
机器学习之支持向量机(Support Vector Machine)
查看>>
模型小型化小结
查看>>
fopen()和fclose()
查看>>
虹软arcface人脸识别集成到项目中
查看>>
[c语言]运算符的优先级与结合性
查看>>
C++ Studio (二) ----- atoi()函数的实现 (自己编写功能)
查看>>
NO.8:绝不在构造或者析构过程中调用virtual函数
查看>>
WinForm 调用WebService 隐藏服务器IP地址之真假美猴王~!O(∩_∩)O哈哈~
查看>>
mysql之命令行导入导出
查看>>
pythonbrew, pythonz, virtualenv
查看>>
没有mysql支持时的替代方案
查看>>