C/C++中函数指针与回调函数

1.函数指针

(1)函数指针的定义

函数:完成某一个特定功能的代码块;

指针:它是一种特殊的变量,用来保存地址值,指针是有类型的,某类型的指针指向某类型的地址

函数指针:顾名思义,通过一个指针指向一个函数的地址

定义格式:返回值 (*指针名)(参数列表);

比如:

int add(int a, int b)
{
    return (a + b);
}

这段代码在编译生成后存储在代码区,而这段代码其实是可以取到地址,而其地址就是函数名。我们可以使用指针存储这个函数的地址—函数指针

对于上述函数的函数指针定义:

int (*p)(int,int)    //参数列表中可以写形参也可以不写
也可以写成
int (*)(int a, int b)

(2)通过函数指针去调用函数

直接像使用函数名一样使用函数指针来调用函数,即:

函数指针名(参数列表)

示例:

#include <stdio.h>

// 两个数相加
int add(int a, int b)
{
	return (a + b);
}

// 求平方
int square(int a)
{
	return (a*a);
}

int main()
{
	int(*p)(int, int);


	//printf("p未做赋值前 = %p\n", p);    //未做赋值p的值是不确定的
	// printf("p未做赋值前 p(1,2) = %d\n",  p(1,2)); 	在定义后未指向任何函数就调用它,程序运行时崩溃

	p = add;   //p指向add函数

	int c = p(2, 300);    //使用p来调用add函数
	printf("c = %d\n", c);

	int(*p_func)(int a) = NULL;   //一般定义的时候可以先初始化为NULL
	p_func = square;       //p指向square函数
	int d = p_func(2);    //使用p来调用square函数
	printf("d = %d\n", d);    

	return 0;
}

C/C++中函数指针与回调函数

(3)函数指针的匹配问题

函数指针的最终目的,就是通过函数指针可以访问到指向的函数,而函数指针是一种强类型指针,定义一定要百分百类型匹配(返回值类型,参数类型,参数个数要相同),最简单的方式就是将函数名换成(*指针名)

但不同函数但函数指针类型一样,那定义的函数指针类型变量可以指向不同的函数,如:

#include <stdio.h>

int add(int a, int b)
{
	return (a + b);
}

int del(int c, int d)
{
	printf("del c = %d, d = %d\n", c, d);
	return (c - d);
}

int del2(int c, char d)
{
	printf("del2 c = %d, d = %d\n", c, d);
	return (c - d);
}

int main()
{
	int(*p_add)(int, int) = NULL;
	int(*p_del)(int, int) = NULL;
	// 运算 不同的函数其函数指针类型可能是一样,具体看 (1)返回值,(2)参数类型,(3)参数个数, 都需要匹配上
	int(*p_opt)(int, int) = NULL;

	int(*p_opt2)(int, char) = NULL;    // 匹配del2

	int ret = 0;

	p_add = add;
	ret = p_add(1, 300);
	printf("p_add(1, 300) = %d\n", ret);

	p_del = del;
	ret = p_del(1, 300);
	printf("p_del(1, 300) = %d\n", ret);

	p_opt = add;        // 加法
	ret = p_opt(1, 300);
	printf("加法 p_opt(1, 300) = %d\n", ret);

	p_opt = del;
	ret = p_opt(1, 300);
	printf("减法 p_opt(1, 300) = %d\n", ret);

	p_opt2 = del2;                   // 不匹配del,只能匹配del2
	ret = p_opt2(1, 100);           // 不匹配
	printf("减法 p_opt2(1, 300) = %d\n", ret);    // 结果错误

	return 0;
}

C/C++中函数指针与回调函数

2.回调函数

(1)什么是回调函数?

回调函数(callback function)指通过函数指针调用的函数!

示例:

#include <stdio.h>

int add(int a, int b)
{
	return (a + b);
}

int del(int a, int b)
{
	return (a - b);
}


// 运算
void opt1(int a, int b, int type)
{
	if (0 == type)       // 加法
	{
		printf("运算结果 = %d\n", add(a, b));
	}
	else if ((1 == type))    // 减法
	{
		printf("运算结果 = %d\n", del(a, b));
	}
	else
	{
		printf("未知的运算方法\n");
	}
}

// 函数指针回调的方式做运算
void opt2(int a, int b, int(*callback)(int, int))
{
	if (callback)
		printf("运算结果 = %d\n", callback(a, b));
}

int main()
{
	// 加法
	opt1(1, 300, 0);
	// 减法
	opt1(1, 300, 1);

	// 新的写法
	printf("\n新的写法\n");
	//加法
	opt2(1, 300, add);
	//减法
	opt2(1, 300, del);

	return 0;
}

C/C++中函数指针与回调函数

上述代码中:

在opt2的写法中,它把具体要调用哪一个函数交给了调用者,而运算函数只提供统一的函数指针接口,把具体的业务放到外面来,运算函数内部根本不知道add和del等函数的存在,实现了隔离

而在opt1的写法中,运算函数是知道add和del等函数的存在的

补充:上述的判断语句if(0 == type)中先写数字的好处:

如果少写了一个=的话编译器会报错,而写在右边不会报错不容易找到错误,所以遇到这种数字的判断就把数字写在等号的前面

(2)回调函数的意义

可以提供统一的调用接口,把具体要做的事情放到回调函数(回调函数做什么工作是根据你应用需求)。比如线程的创建,但线程具体的工作需要在回调函数中指定。

示例:

#include <stdio.h>
#include <windows.h>

DWORD WINAPI ThreadFunc1(LPVOID p)
{
    printf("我是Darren老师,我要撸代码了\n");
    int i = 0;
    while (1)
    {
        if(i++ > 5)
        {
            break;  // 退出线程
        }
        printf("我是Darren老师,写了%d行代码了\n", i*100);
        Sleep(1000);
    }
    printf("我是Darren老师,代码撸完了\n");
    return 0;
}

DWORD WINAPI ThreadFunc2(LPVOID p)
{
    printf("我是Martin老师,我要上Linux公开课了\n");
    
    int i = 0;
    while (1)
    {
        if(i++ > 10)
        {
            break;  // 退出线程
        }
        printf("我是Martin老师,上了%d分钟课了\n", i*5);
        Sleep(1000);
    }
    printf("我是Martin老师,公开课上完了\n");
    return 0;
}

int main()
{
    HANDLE hThread1;
    DWORD  threadId1;
    
    HANDLE hThread2;
    DWORD  threadId2;
    
    
    hThread1 = CreateThread(NULL, 0, ThreadFunc1, 0, 0, &threadId1); // 创建线程
    hThread2 = CreateThread(NULL, 0, ThreadFunc2, 0, 0, &threadId2); // 创建线程
    printf("两个线程创建完毕\n");
    
    WaitForSingleObject(hThread1, INFINITE);//等待线程返回,用sleep()就太山寨了
    WaitForSingleObject(hThread2, INFINITE);
    CloseHandle(hThread1);  // 释放资源
    CloseHandle(hThread2);
    
    
    printf("程序结束\n");
    
    return 0;
}

C/C++中函数指针与回调函数

图示理解:

C/C++中函数指针与回调函数

在Linux下也是一样,创建一个线程也是设置一个回调函数,让不同的回调函数指向不同的业务