摘要:总结了函数类型的本质,回调函数的思想,以及负责指针阅读技巧右左法则。
一、函数类型
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>
其实我也没怎么理解右左法则的精髓,分析起来比较难,不过函数指针的基础倒还是掌握了,这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!