浅析函数指针与回调函数

一、函数指针

1、函数地址

        指针也就是内存地址,指针变量是用来存放内存地址的变量。
我们常见的指针有int*,char*等等。其实函数也是有指针的,我们称之为函数指针。
比如说下面的函数
int myFunction(int a)
{
    printf("%d\n",a);
    return 0;
}
正常调用代码是这样的
myFunction(10);
但如果我们这样写运行出错吗?
myFunction;
结果是并不出错,语法也认,虽然它没有括号,也没写参数。那么它到底是什么呢?让我们测试一下
printf("%p\n",myFunction);

#include 

int myFunction(int a)
{
	printf("%d\n", a);
	return 0;
}

int main()
{
	//myFunction(10);
	//myFunction;
	printf("%p\n", myFunction);

	return 0;
}

结果如下:

浅析函数指针与回调函数_第1张图片

发现它返回的是一个内存地址。我们知道函数是存放在内存四区中的代码区,既然存放在内存中自然有地址了。

只写一个函数名返回的就是该函数在内存中的首地址】,请记住这句话,很重要。

2、函数指针语法

        函数指针的语法: 函数返回值 (*指针名)(函数参数1...)   

         挺怪异的吧,(*指针名)必须有括号,否则指针名就与后面的函数参数那对括号先结合了,那就完全不对了。

普通指针写法是: int* p1;
函数指针写法是: int (*p2)(int);    // 用前面的myFunction举例
p2就是函数指针了,当然这个p2是随便起的名字了,与前面的myFunction名字无任何关系。
看下面的代码
int a = 10;
int b = 20;
int* p1 = NULL;
p1 = &a;
p1 = &b;
这里的指针p1并不是固定指向某个变量,它是针对所有的int类型变量的,它是可以来回切换的。
那么函数指针也并不是只指向某个具体的函数,也是针对某种类型的函数的。
比如下面的函数就都是属于同一类型的,这里就只写函数定义了,方便看
void f1(int a,char c);
void f2(int aa,char cc);
void f3(int aaa,char ccc);
参数中的a,aa,aaa,c,cc,ccc是可以省略的。
void f1(int,char);
void f2(int,char);
void f3(int,char);
看看这3个函数除了函数名不同外其它都是一样的,这就属于是同类函数(无返回值,2个参数,int与char)。

3、函数指针指向与调用

        我们可以写一个函数指针分别指向这3个函数。
void (*pFun)(int,char);    // 定义函数指针,起名为pFun
pFun = f1;        // 指向第1个函数(指向时只要函数名,千万不要写函数后面的括号与参数,因为只有函数名才返回该函数的地址)
pFun(10,'A');    // 开始调用
pFun = f2;        // 指向第2个函数
pFun(20,'B');    // 开始调用
pFun = f3;        // 指向第3个函数
(*pFun)(30,'C');// 开始调用
我们发现定义好函数指针的函数地址指向后调用方式有二种,一种是正常调用,就把函数指针当成一个正常的函数用,另一种是(*pFun),把函数指针先解引用,那不就是函数了嘛,然后后面加括号参数调用。
下面是具体的调用代码

#include 

void f1(int a, char c)
{
	printf("这是f1函数,传入参数为 %d %c\n", a, c);
}
void f2(int aa, char cc)
{
	printf("这是f2函数,传入参数为 %d %c\n", aa, cc);
}
void f3(int aaa, char ccc)
{
	printf("这是f3函数,传入参数为 %d %c\n", aaa, ccc);
}

int main()
{
	// 定义函数指针,起名为pFun
	void (*pFun)(int, char);

	// 指向第1个函数(指向时只要函数名,千万不要写函数后面的括号与参数,因为只有函数名才返回该函数的地址)
	pFun = f1;
	pFun(10, 'A');	// 开始调用(把函数指针当作正常的函数名来使用)

	// 指向第2个函数
	pFun = f2;
	pFun(20, 'B');	// 开始调用(把函数指针当作正常的函数名来使用)

	// 指向第3个函数
	pFun = f3;
	(*pFun)(30, 'C');// 开始调用(先把函数指针解引用,再使用)

	return 0;
}

结果如下:

浅析函数指针与回调函数_第2张图片

4、函数指针的意义

        普通指针主要是用来作为函数的参数来用的,因为传递的是地址,所以在函数中做出的任何变化都会反映到该地址所对应的变量值中去。
函数指针也是一样的,也是作为函数的参数来用的,这种作为另一个函数参数的函数指针所指向的函数我们称呼为回调函数。

二、回调函数

        回调函数写法很简单,重要的是应用场景。在C++中多常见于for_each遍历函数中。
在这里我们就瞎编一个例子吧

#include 

// 敌方结构体
struct _stu_enemy
{
	char name[31];	// 名字
	int hp;			// 生命
};

// 伤害函数
void hurt(struct _stu_enemy* enemy, int (*myFun)(int))
{
	printf("战斗前%s的生命值:%d\n", enemy->name, enemy->hp);

	// 血拼到底
	int newHp = myFun(enemy->hp);
	if (newHp < 0) { newHp = 0; }
	enemy->hp = newHp;

	printf("战斗后%s的生命值:%d\n", enemy->name, enemy->hp);

	if (enemy->hp > 0)
	{
		printf("%s:我还活着,哈哈哈,还有谁!\n",enemy->name);
	}
	else
	{
		printf("%s:电脑突然断电了,这不是我得错!\n", enemy->name);
	}
	printf("=============================\n");
}

// 蛮王伤害(这是蛮王的回调函数)
int manwangHurt(int enemyLife)
{
	printf("蛮王:我是5秒真男人...(寒冰:给老娘滚)\n");
	enemyLife -= 50;
	return enemyLife;
}

// 石头伤害(这是石头的回调函数)
int shitouHurt(int enemyLife)
{
	printf("石头:我也是从石头里蹦出来的,怎么就不是猴子呢???\n");
	enemyLife -= 60;
	return enemyLife;
}

int main()
{
	// 初始化对手
	struct _stu_enemy enemy = { "瑞文",100 };

	// 蛮王
	hurt(&enemy, manwangHurt);

	// 石头
	hurt(&enemy, shitouHurt);

	return 0;
}

运行结果如下:

浅析函数指针与回调函数_第3张图片

你可能感兴趣的:(C/C++语言,c语言)