目录
一. 函数指针: 什么是函数指针?
函数指针的三种定义方式:
(1)先定义出函数的类型,再通过类型定义函数指针变量
(2)先定义出函数指针的类型,再通过指针类型定义函数指针变量
(3)重点:直接定义函数指针变量
函数指针和指针函数的区别:
二. 回调函数
实例一:
实例二:实例一的具体实现。
实例三:固件开发中使用到的一个回调函数实例。
三. 结构体指针
示例1:结构体指针的使用
示例2:指向结构体变量的指针 & 结构体嵌套
示例3:结构体数组指针的使用
示例4:结构体指针作为函数参数
指向函数入口地址的指针。
如果在程序中定义了一个函数,那么编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址,函数名表示的就是这个地址。
既然是地址,就可以定义一个指针变量来存放,这个指针变量就叫做函数指针变量。
函数指针的定义:
# 返回值类型 +(指针变量名)(形参列表)
eg:
int (*p)(int, int);
//定义出一个函数类型,返回值是void,形参列表(int,char)
typedef void(FUNC_TYPE)(int, char);
FUNC_TYPE * pFunc = func;
typedef void(*FUNC_TYPE)(int, char);
FUNC_TYPE pFunc = func;
void(*p) (int, char) = func;
解释:
定义了一个指针变量 p ,该指针变量可以指向返回值类型为 void,且有两个整型参数的函数。p 的类型为 void(*)(int, char)。
注意:
(*p)两端的括号不能去掉。如果省略了括号,就变成一个函数声明了,即声明了一个返回值类型为指针型的函数,即变成了指针函数。
# 指针函数本质是一个函数,其返回值是一个指针: int* p(int, int); # 函数指针本质是一个指针,其指向一个函数: int (*p)(int, int);
简单点就是:函数名带括号的就是函数指针,否则就是指针函数。
同理,数组指针和指针数组的区别:
# 数组指针是一个指针,指向一个数组: int (*p)[ ]; # 指针数组是一个数组,数组的元素都是指针: int* p[ ];
以下实例声明了函数指针变量p,指向函数func_max:
#include
int func_max(int x, int y)
{
return x>y ? x:y; // ?:是条件运算符,如果条件为真,则值为 x ,否则值为 y。
}
int main(void)
{
int (*p)(int, int)= &func_max; //p是函数指针,&可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", &a, &b, &c);
d=p(p(a,b),c); //与直接调用函数等价,d=max(max(a,b),c)
printf("最大的数字是: %d\n", d);
return 0;
}
输出为:
请输入三个数字:1 2 3
最大的数字是: 3
函数指针变量可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数。
也就是,你写一个函数A,并把这个函数A的地址赋值给一个函数指针,然后把这个函数指针当作参数赋给另一个函数B,函数B通过函数指针来调用函数A。这个函数A就是回调函数。
为什么要使用回调函数呢?
答:解耦。
#include
#include // 包含Library Function所在的Software library库的头文件
int Callback() // Callback Function
{
// TODO
return 0;
}
int main() // Main program
{
// TODO
Library(Callback);
// TODO
return 0;
}
仔细一看可以发现:在回调中,主程序把回调函数像参数一样传入库函数。这样我们只需改变传进库函数的参数,就可以实现不同的功能,并且丝毫不需要修改库函数的实现,这就是解耦。
回调函数的作用
比如,我们写A B C D 四个函数,封装成一个库文件,然后我们的主函数里面要写一个功能函数,这个功能要用到函数A,假如不用函数指针,这个功能函数就要调用函数A,下次如果用到函数B,那么我们得删掉A,调用函数B,每次都要修改这个函数很麻烦,但如果使用回调函数就不一样了,我们可以定义4个函数指针,把4个函数的地址分别赋给4个函数指针,然后将函数指针当作参数传递给功能函数,功能函数就可以通过修改参数来调用对应的函数,而它本身不用做任何的修改。这样的话,功能函数就可以根据不同的情况,通过函数指针去调用不同的函数,代码如下:
#include
#include
# 4个回调函数
float ADD(float a, float b)
{
return a + b;
}
float SUB(float a, float b)
{
return a - b;
}
float MUL(float a, float b)
{
return a * b;
}
float DIV(float a, float b)
{
return a / b;
}
# 4个函数指针
float (*A)(float x, float y) = ADD;
float (*B)(float x, float y) = SUB;
float (*C)(float x, float y) = MUL;
float (*D)(float x, float y) = DIV;
# 函数指针作为函数参数,以此来调用回调函数
float fun(float x, float y, float(*p)(float x, float y))
{
return p(x, y);
}
int main()
{
printf("%f", fun(2, 3, A));
}
/** \brief function pointer type to void/void function **/
typedef void (*func_ptr_t)(void); # 定义一个函数指针类型
/** \brief IRQ registration structure definition **/
typedef struct stc_irq_regi_conf
{
en_int_src_t enIntSrc;
IRQn_Type enIRQn;
func_ptr_t pfnCallback; # 定义一个函数指针变量
}stc_irq_regi_conf_t;
/** \brief generic error codes **/
typedef enum en_result
{
Ok = 0u;
Error = 1u;
ErrorTimeout = 2u;
ErrorNotReady = 3u;
}en_result_t;
/**
* @brief Timer0 interrupt callbackfunc
*/
static void Timer0_CallBack(void)
{
//ToDo
}
void TIM0_Init(void)
{
stc_irq_regi_conf_t stcIrqRegiConf;
/* Register TMR_INI_GCMA Int to Vect.No.001 */
stcIrqRegiConf.enIRQn = TIMER01_CHA_IRQn;
/* Select I2C Error or Event interrupt function */
stcIrqRegiConf.enIntSrc = TMR_INI_GCMA;
/* Callback function */
stcIrqRegiConf.pfnCallback =&Timer0_CallBack; # 将回调函数的地址赋给函数指针
/* Registration IRQ */
enIrqRegistration(&stcIrqRegiConf);
}
/**
*******************************************************************************
** \brief IRQ Registration
**
** param [in] pstcIrqRegiConf, IRQ registration
** configure structure
**
** retval Ok, IRQ register successfully.
** ErrorInvalidParameter, IRQ No. and
** Vector No. are not match.
** ErrorUninitialized, Make sure the
** Interrupt select register (INTSEL) is
** default value (0x1FFu) before setting.
**
*****************************************************************************/
# 函数指针作为该函数参数,通过函数指针调用回调函数
en_result_t enIrqRegistration(const stc_irq_regi_conf_t *pstcIrqRegiConf)
{
//库函数IRQ Registration
}
当一个指针变量指向结构体时,就称它为结构体指针。其定义方式为:
struct 结构体名 *变量名;
定义结构体指针的 eg 如下:
typedef struct stu{ //定义结构体
char *name;
int age;
char group;
float score;
}stu;
stu stu1 = {"Tom", 18,'A', 136.5};
stu *pstu = &stu1; //结构体指针
注意:
结构体变量名和数组名不同。数组名在表达式中会被转换为数组指针,而结构体变量名不会,要想取得结构体变量的地址,必须在前面加 & ,如下所示:
struct stu *pstu = &stu1;
还应该注意,结构体和结构体变量是两个不同的概念:结构体是一种数据类型,就像int、float这些关键字本身不占用内存一样;结构体变量才包含数据,才需要内存来存储。比如下面这种写法,是错误的,不可能去取一个结构体名的地址。
struct stu *pstu = &stu;
#include
typedef struct stu
{
char *name;
int num;
char group;
float score;
}stu, *pstu;
int main()
{
stu stu1 = {"Tom", 12, 'A', 136.5};
pstu pstu1 = &stu1;
printf("%s %d %c %.1f \n", stu1.name, stu1.num, stu1.group, stu1.score);
printf("%s的学号是%d,在%c组,今年的成绩是%f.\n", pstu1->name, pstu1->num, pstu1->group, pstu1->score);
return 0;
}
输出为:
Tom的学号是12,在A组,今年的成绩是136.5。
#include
#include
typedef struct AGE
{
int year;
int month;
int day;
}age;
typedef struct STUDENT
{
char name[20];
int num;
age birthday;
float score;
}student;
int main(void)
{
student student1; //用struct STUDENT结构体类型定义结构体变量student1
#if 0
student* p = NULL; //定义一个指向struct STUDENT结构体类型的指针变量p
p = &student1; // *p指向结构体变量student1的首地址,即第一个成员的地址
#else
student* p = &student1;
#endif
strcpy(p->name,"小明"); //(*p).name等价于student1.name
p->birthday.year = 1994;
p->birthday.month = 9;
p->birthday.day = 18;
p->num = 1207041;
p->score = 100;
printf("name: %s\n", p->name);
printf("birthday: %d-%d-%d\n", p->birthday.year, p->birthday.month, p->birthday.day);
printf("num: %d\n", p->num);
printf("score: %.1f\n", p->score);
return 0;
}
输出为:
name: 小明
birthday: 1994-9-18
num: 1207041
score: 100.0
如果定义一个结构体指针变量,并把结构体数组的数组名赋给这个指针变量的话,就意味着将结构体数组的第一个元素,即第一个结构体变量的地址,也即第一个结构变量中的第一个成员的地址赋给了这个指针变量。
#include
struct stu{
char *name;
int num;
char group;
float score;
};
int main(void)
{
struct stu stus[] = {{"jack", 5, 'A', 12.5},{"rose", 4, 'B', 123.5},{"paul", 3, 'C', 1234.5}};
struct stu *p = stus; //定义结构体数组指针p,并指向结构体数组的第一个元素即stus[0].
int len = sizeof(stus)/sizeof(struct stu);
for(p = stus; p < stus+len; p++) // p+1 就指向stus[1]。
{
printf("%s\t\t%d\t%c\t%f\n", p->name, p->num, p->group, p->score);
}
return 0;
}
输出为:
jack 5 A 12.500000
rose 4 B 123.500000
paul 3 C 1234.500000
如果结构体成员较多,尤其是成员为数组时,传送的时间和空间开销会很大,影响程序效率。所以最好的办法就是使用结构体指针。
eg:计算全班学生的总成绩、平均成绩、140分以下的人数。
#include
typedef struct stu
{
char* name;
int num;
char group;
float score;
}stu;
stu stus[] = {
{"jack", 5, 'A', 12.5},
{"rose", 4, 'B', 123.5},
{"paul", 3, 'C', 1234.5}
};
void average(struct stu* ps, int len);
int main()
{
int len = sizeof(stus) / sizeof(struct stu);
average(stus, len);
return 0;
}
void average(struct stu* ps, int len)
{
int i, num_140 = 0;
float average, sum = 0;
for (i = 0; i < len; i++)
{
sum += (ps + i)->score;
if ((ps + i)->score < 140)
{
num_140++;
}
}
printf("sum=%.2f\naverage=%.2f\nnum_140=%d\n", sum, sum / 5, num_140);
}
输出为:
sum=1370.50
average=274.10
num_140=2