C语言学习笔记(21) 函数指针、回调函数及复杂指针阅读技巧

摘要:总结了函数类型的本质,回调函数的思想,以及负责指针阅读技巧右左法则。


一、函数类型

    1.C语言中的函数也有自己的类型,就像数组一样。

    2.C语言中国的函数的类型由函数返回值参数类型参数个数共同决定,甚至包括参数的顺序

    3.例如:int add(int i,int j);的类型为int(int,int).

int func1(double I,int j)和int func2(int j,double i)其实是两个不同的函数类型,因为参数的顺序是不同的。

    4.C语言中可以使用typedef来给函数的类型重新命名,这跟之前给数组一样。

       typedef type name(paramenterlist)

    例如:

    typedef int f(int,int);

    typedef void p(int);


二、函数指针

    1.函数指针用于指向一个函数。

    2.函数名是执行函数体的入口地址,这和数组名代表数组首元素的地址类似,可以定义指针来保存这个地址。

    3.可通过函数类型定义函数指针:FuncType* pointer;

    4.也可以直接定义:type(*pointer)(paramenter list)

       pointer为函数指针名。

       type为指向函数的返回值类型 。

       paramenterlist为指向函数的参数类型列表。

 

例子1:函数指针的使用

<span style="font-size:18px;">#include <stdio.h>
 
typedef int(FUNC)(int);//定义一个函数类型,名称为FUNC,返回值为int,参数个数为1,类型为int
 
int test(int i)
{
   return i * i;
}
 
void f()
{
   printf("Call f()...\n");
}
 
int main()
{
   FUNC* pt = test;//利用定义的函数类型定义函数指针,指向test函数
   
   void(*pf)() = &f;//直接定义一个函数指针,指向f函数,两种方法异曲同工
    
   pf();
   (*pf)();//调用的两种不同方法
   
   printf("Function pointer call: %d\n", pt(2));//这里pt(2),是在调用的时候,把2这个参数传过去
}</span>

    这个例子反映出来的问题就是函数名就是代表函数的地址,可以把函数名代表的地址赋值给一个指针,关于函数指针的定义的两种方法也在程序里体现了。在这里对函数取地址符和不取地址符,效果是一样的。


三、回调函数(驱动和内核中常见的一种机制)

    1.回调函数是利用函数指针实现的一种调用机制。

    2.回调机制原理:

       调用者不知道具体事件发生时需要调用的具体函数。

       被调用者不知何时被调用,只知道被调用后需要完成的任务。

       当具体事件发生的时候,调用者通过函数指针调用具体函数。

    3.回调机制将调用者和被调用者分开,不相互依赖。

    相信做过驱动的朋友对这种机制都不陌生,我们的驱动就是不知道具体做什么,但是会打开应用层传递过来的具体事项,去实现一个功能,用的时候注册,不用的时候卸掉,两者不相互依赖。


例子2:回调函数思想

<span style="font-size:18px;">#include <stdio.h>
 
typedef int(*FUNCTION)(int);//定义一个函数指针
 
int g(int n, FUNCTION f)//g中第二个参数,并不知道具体的函数是什么样子的,而是需要的时候将各个函数的地址传进去
{
   int i = 0;
   int ret = 0;
   
   for(i=1; i<=n; i++)
    {
       ret += i*f(i);
    }
   
   return ret;
}
 
int f1(int x)//三个函数实现不同的算式
{
   return x + 1;
}
 
int f2(int x)
{
   return 2*x - 1;
}
 
int f3(int x)
{
   return -x;
}
 
int main()
{
   printf("x * f1(x): %d\n", g(3, f1));
   printf("x * f2(x): %d\n", g(3, f2));
   printf("x * f3(x): %d\n", g(3, f3));
}</span>

    其实,这里只是一个函数指针的简单应用,但是其中的思想比较重要,写过驱动读过LDDR3两本书的应该都能体会,在驱动和Linux内核中,这样的思想无处不在。

 

四、复杂指针阅读技巧

    右左法则:

    1.从最里层的圆括号中未定义的标识符看起。

    2.首先往右看,再往左看。

    3.当遇到圆括号或者方括号的时候,可以确定部分类型,并调转方向。

    4.重复2,3步骤,知道阅读结束。

    看起来很拗口,这里以一个例子来解释。

 

例子3:右左法则阅读复杂指针

<span style="font-size:18px;">#include <stdio.h>
 
/* 
    1.从最里层的圆括号中未定义的标识符看起。
    2.首先往右看,再往左看。
    3.当遇到圆括号或者方括号的时候,可以确定部分类型,并调转方向。
    4.重复2,3步骤,知道阅读结束。
*/
 
int main()
{
/*按照第一步,从最里层看是看,这里的最里层我认为是从左往右,也就是p2,第一个未定义的标识符
    然后往右看,遇到p2后面的圆括号了,往左看,遇到p2前面的圆括号,这时候圆括号里面就可以确定一个类型,p2是一个指针,指向什么?
    往后右看,确定这是一个函数指针,里面的参数也是类似的方法,一个是Int类型的指针,后面一个是一个函数指针参数。
    其实这个不用这个右左法则也看的出来吧,哈哈。
*/ 
   int (*p2)(int*, int (*f)(int*));
   
/*
    还是先看p3[5],有五个元素,每个元素都是一个函数指针。话说右左法则还不如自己理解方便啊。
*/   
   int (*p3[5])(int*);
/*
    这是个数组指针,指向一个数组,每个数组中有五个元素,每一个元素都是一个指针,指向一个函数,函数类型为int,参数为int*
*/   
   int (*(*p4)[5])(int*);
}</span>

    其实我也没怎么理解右左法则的精髓,分析起来比较难,不过函数指针的基础倒还是掌握了,这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值