文章目录
一、函数指针
1.函数类型(模板)
1.1 如何区分两个不同的函数?
(1)一个函数在编译时被分配一个入口地址,这个地址就称为函数的指针,函数名代表函数的入口地址。
测试:将main函数的地址打出来
int main()
{
printf("%p\n",main);
return 0;
}
结果:得到的也是一个地址,但是不要将括号也写进去,在打印的时候。
(2)函数三要素: 名称、参数、返回值。C语言中的函数有自己特定的类型。
c语言中通过typedef为函数类型重命名:然后用这个函数类型来定义函数指针。
typedef int f(int, int); // f 为函数类型
typedef void p(int); // p 为函数类型
这一点和数组一样,因此我们可以用一个指针变量来存放这个入口地址,然后通过该指针变量调用函数。
1.2 函数指针使用的注意事项
注意:通过函数类型定义的变量是不能够直接执行,因为没有函数体。只能通过类型定义一个函数指针指向某一个具体函数,才能调用。
(1)测试代码:
typedef void(p)(int, int);
void my_func(int a,int b){
printf("%d %d\n",a,b);
}
void test(){
p p1;
//p1(10,20); //错误,不能直接调用,只描述了函数类型,但是并没有定义函数体,没有函数体无法调用
p* p2 = my_func;
p2(10,20); //正确,指向有函数体的函数入口地址
}
测试结果:
1.3总结定义函数指针的步骤
1.2 的测试代码,我们课将定义函数指针的步骤总结出来:
(1)观察你要指向的函数,将函数的类型(或者说模板)提取出来。这是什么意思,就是将要指向的函数的返回值,参数类列表提取出来。
void my_func(int a,int b)
返回值是 void ,参数列表是 int,int。
(2)将函数的模板提取出来以后,我们就可以为这个模板起一个别名,这样方便以后定义函数指针。
typedef void(p)(int, int);
这里,我们为这个模板起了一个别名,叫做p。将函数模板提取出来,起别名,这样做有什么好处呢?我们用这个函数模板定义一个指针 ,如下:
p * p2;
那么这个指针,就可以指向符合我们提取出来的函数模板的函数,意思是不止可以指向一个函数,而是是一个种类的函数,是多个,只要那个函数符合函数的模板就行,返回值是void,参数列表是int,int。但是注意一次(同一时刻)只能指向一个具体函数的函数。
或者说我们可以定义多个这种函数类型的指针变量来指向多个函数,就像使用 int 定义多个int 变量。
(3)指向具体的函数,符合模板的函数。
p* p2 = my_func;
再次测试:
// 定义一种函数类型,或者说函数模板
typedef int(p_Func)(int ,int );
int test_3(int a,int b)
{
printf("a+b=%d\n",a+b);
}
int test_4(int a,int b)
{
printf("a*b=%d\n",a*b);
}
void testFunctionPoint()
{
p_Func *p_test_3 = test_3; // 用自定义的或者抽取出
来的函数模板,定义函数指针,指向test_3()
p_Func *p_test_4 = test_4;
p_test_3(4,5);
p_test_4(6,7);
}
测试结果:
注意下面这两个函数,都是符合函数模板的:返回值,int ;参数列表为int ,int 。
int test_3(int a,int b);
int test_4(int a,int b);
2.函数指针(指向函数的指针)三种定义方式
除了上面的定义函数指针的方法,还有其他的两种,但思路都是大同小异,都是要提取函数模板(类型)。下面就将函数指针的三种定义方法都列出来。要多注意第二种方式,抽取了函数模板,然后就给函数模板指针起别名。
(1)函数指针定义方式(先定义函数类型,根据类型定义指针变量);
(2)先定义函数指针类型,根据类型定义指针变量;
(3)直接定义函数指针变量;
int my_func(int a,int b){
printf("ret:%d\n", a + b);
return 0;
}
//1. 先定义函数类型,通过类型定义指针
void test01(){
typedef int(FUNC_TYPE)(int, int);
FUNC_TYPE* f = my_func;
//如何调用?
(*f)(10, 20);
f(10, 20);
}
//2. 定义函数指针类型
void test02(){
typedef int(*FUNC_POINTER)(int, int);
FUNC_POINTER f = my_func;
//如何调用?
(*f)(10, 20);
f(10, 20);
}
//3. 直接定义函数指针变量
void test03(){
int(*f)(int, int) = my_func;
//如何调用?
(*f)(10, 20);
f(10, 20);
}
二、函数指针做参数(回调函数)
函数参数除了是普通变量,还可以是函数指针变量。函数指针做函数的参数,通常称为回调函数。
//形参为普通变量
void fun( int x ){}
//形参为函数指针变量
void fun( int(*p)(int a) ){}
函数指针变量常见的用途之一是把指针作为参数传递到其他函数,指向函数的指针也可以作为参数,以实现函数地址的传递。
测试代码:
#include <stdio.h>
#include <string.h>
typedef int (*p_Func)(int,int);
int Add(int a,int b)
{
return a+b;
}
//void CallAdd(int (*p_add)(int,int),int c)
void CallAdd(p_Func p_add,int c)
{
int sum = 0;
sum = p_add(5,6) + c;
printf("sum =%d\n",sum);
}
void test_1()
{
//typedef int (*p_Func)(int,int);
// 定义一个函数指针,指向Add函数
p_Func p_add = Add;
CallAdd(p_add,3);
}
int main()
{
test_1();
return 0;
}
测试结果:
1.注意事项
当我们在开头给函数指针起了别名之后,就可以把函数指针当成一种数据类型,就像是普通的数据类型,比如 像int一样去声明或者定义变量。
typedef int (*p_Func)(int,int);
所以对于调用Add 的函数 CallAdd 函数的参数可以有一下两种写法:
void CallAdd(int (*p_add)(int,int),int c);
void CallAdd(p_Func p_add,int c);