一、目的
在RTOS中创建任务时一般会要求实现具体任务函数,例如在RT-Thread中任务创建函数声明如下
/**
* @brief This function will create a thread object and allocate thread object memory.
* and stack.
*
* @param name is the name of thread, which shall be unique.
*
* @param entry is the entry function of thread.
*
* @param parameter is the parameter of thread enter function.
*
* @param stack_size is the size of thread stack.
*
* @param priority is the priority of thread.
*
* @param tick is the time slice if there are same priority thread.
*
* @return If the return value is a rt_thread structure pointer, the function is successfully executed.
* If the return value is RT_NULL, it means this operation failed.
*/
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick);
void (*entry)(void *parameter)
严格的讲,这边的entry是一个函数指针类型变量,很多小伙伴可能暂时无法理解这个入参的声明方式
例如我们定义的任务执行函数的伪代码如下:
static void my_task(void *args) {
while (1) {
//任务循环
}
}
此处的my_task就是我们定义的任务函数,没有返回值,只有一个void *的函数入参;任务一般都是一个while循环,例如执行具体的业务逻辑。
现在我们任务执行函数也有了,那么如何创建一个新的任务执行呢?
下面的代码片段就是创建一个新的任务
rt_thread_t my_task_t = rt_thread_create("mytask", my_task, 1024, 3, 5);
现在请大家分析下面的代码片段:
typedef int (*foo)(int *);
int (*foo)(int *);
int foo(int *);
int *foo(int *);
如果你能够很清楚的辨别其中的区别,那么本篇博文介绍的知识你已经完全掌握;否则请跟我的步伐继续走下去,相信你会收获颇丰。
在看完本篇之后肯定也就能够看明白下面的函数声明
void ( *signal(int signum, void (*handler)(int)) ) (int);
二、介绍
在c语言中我们有各种类型包括整形(int/long)、字符类型(char)、指针类型、结构体类型。
其中指针类型细分下来就有int*、char*、各个结构体指针等,还有一种大家其实可能都用过但是没有深究的函数指针类型。
函数指针类型本质是一个指针,只是这个指针指向一个函数(函数名其实就是一个地址)。
typedef(类型重定义)
有时候一些类型名字比较繁琐或者复杂我们可能会使用typedef重新定义,例如
typedef unsigned char uint8_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef struct foo {
//struct field
} foo_t;
//下面的两行代码效果一样,也就是foo_t等效于struct foo
struct foo a;
foo_t a;
函数指针类型
typedef int (*pfunction)(int *);
上面的代码片段就是一个函数指针类型的声明,仅接受一个int *的入参,并且返回一个int类型的返回值。
注意包围pfunction的(*)
既然我们声明了一个函数指针类型,那也就可以声明一个函数指针类型的变量,这个变量可以指向一个函数。
下面声明了pfunction类型的变量myfunction
pfunction myfunction;
函数指针变量
int (*myfunction)(int *);
此处的myfunction是一个变量,只是这个变量是一个函数指针类型的,只有一个入参int *和一个int的返回值。
//函数定义
int function_one(int *a) {
return 1;
}
//函数指针变量
int (*myfunction)(int *);
int main() {
myfunction = function;
}
既然myfunction是变量那么在编译后myfunction的类型是Data段,但是function_one因为是函数,所以其编译后的类型就是Code段。
函数声明
int foo(int *);
int *foo(int *);
这边foo是两个函数声明,第一个函数声明只有一个入参int*和一个int类型的返回值;第二个函数只有一个入参int*和一个int*类型的返回值。
现在我们再回头看一下文章开头的函数声明
void ( *signal(int signum, void (*handler)(int)) ) (int);
我们可以重新声明这个函数
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
通过typedef我们重新定义了sighander_t这个函数指针类型,只有一个int入参,没有返回值;signal函数声明有两个入参,一个是int类型,一个是sighander_t函数指针类型;然后这个函数返回一个sighander_t类型的函数指针。
最后,函数指针类型的变量有一个大家耳熟能详的名字就是回调函数。
至此,函数指针类型和函数指针变量就讲解结束了。