C 高级编程day06——函数指针和回调函数

一、函数指针

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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值