Understanding typedefs for function pointers in C
当我阅读其他人的代码时,我总是有点困惑,因为这些代码中有指向带有参数的函数的指针的typedef。我记得我花了一段时间来理解这样一个定义,同时试图理解不久前用C语言编写的一个数值算法。那么,你能分享一下你的技巧和想法吗?如何为函数指针(做的和不做的)编写好的typedef,为什么它们有用,以及如何理解别人的工作?谢谢!
考虑C标准中的
1 | extern void (*signal(int, void(*)(int)))(int); |
非常明显-它是一个函数,接受两个参数,一个整数和一个指向一个函数的指针,该函数接受一个整数作为参数,不返回任何内容,它(
如果你写:好的。
1 | typedef void (*SignalHandler)(int signum); |
然后您可以将
1 | extern SignalHandler signal(int signum, SignalHandler handler); |
这意味着同样的事情,但通常被认为更容易阅读。更清楚的是,该函数接受一个
不过,这需要一点习惯。但是有一件事你不能做,那就是在函数定义中使用
我还是老派,喜欢调用函数指针:好的。
1 | (*functionpointer)(arg1, arg2, ...); |
现代语法仅使用:好的。
1 | functionpointer(arg1, arg2, ...); |
我知道为什么会这样-我只想知道我需要查找变量的初始化位置,而不是一个名为
山姆评论说:好的。
I have seen this explanation before. And then, as is the case now, I think what I didn't get was the connection between the two statements:
Ok.
1
2
3
4 extern void (*signal(int, void()(int)))(int); /*and*/
typedef void (*SignalHandler)(int signum);
extern SignalHandler signal(int signum, SignalHandler handler);Or, what I want to ask is, what is the underlying concept that one can use to come up with the second version you have? What is the fundamental that connects"SignalHandler" and the first typedef? I think what needs to be explained here is what is typedef is actually doing here.
Ok.
让我们再试一次。第一个是直接从C标准中提取的-我重新输入了它,并检查了括号是否正确(直到我更正了它-这是一个很难记住的cookie)。好的。
首先,记住
a pointer to a function that takes an integer as an argument and returns nothing.
Ok.
"无回报"部分拼写为
1 | type (*function)(argtypes); |
在创建了信号处理程序类型之后,我可以使用它来声明变量等等。例如:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | static void alarm_catcher(int signum) { fprintf(stderr,"%s() called (%d) ", __func__, signum); } static void signal_catcher(int signum) { fprintf(stderr,"%s() called (%d) - exiting ", __func__, signum); exit(1); } static struct Handlers { int signum; SignalHandler handler; } handler[] = { { SIGALRM, alarm_catcher }, { SIGINT, signal_catcher }, { SIGQUIT, signal_catcher }, }; int main(void) { size_t num_handlers = sizeof(handler) / sizeof(handler[0]); size_t i; for (i = 0; i < num_handlers; i++) { SignalHandler old_handler = signal(handler[i].signum, SIG_IGN); if (old_handler != SIG_IGN) old_handler = signal(handler[i].signum, handler[i].handler); assert(old_handler == SIG_IGN); } ...continue with ordinary processing... return(EXIT_SUCCESS); } |
请注意如何避免在信号处理程序中使用
那么,我们在这里做了什么——除了省略4个标准头之外,这些头将需要使代码干净地编译?好的。
前两个函数是接受单个整数而不返回任何值的函数。由于
然后,我创建一个结构数组,其中每个元素标识一个信号号和为该信号安装的处理程序。我选择担心3个信号;我经常担心
然后程序执行其任务并正常退出。好的。
请注意,函数名可以被视为指向适当类型函数的指针。如果不应用函数调用括号(例如初始值设定项中的括号),则函数名将变为函数指针。这也是为什么通过
因此,到目前为止,我已经证明了
现在我们回到这个问题上——
让我们回顾一下第二个声明:好的。
1 | extern SignalHandler signal(int signum, SignalHandler handler); |
如果我们像这样更改了函数名和类型:好的。
1 | extern double function(int num1, double num2); |
如果把它解释为一个以
现在,
也可被视为:好的。
1 | extern void (*signal(int signum, void(*handler)(int signum)))(int signum); |
很难解释-所以我可能会搞砸的。这一次我已经给出了参数的名称——尽管这些名称并不重要。好的。
一般来说,在C语言中,声明机制是这样的,如果你写:好的。
1 | type var; |
然后当你写
1 2 3 4 | int i; // i is an int int *ip; // *ip is an int, so ip is a pointer to an integer int abs(int val); // abs(-1) is an int, so abs is a (pointer to a) // function returning an int and taking an int argument |
在标准中,
1 | typedef void (*SignalHandler)(int signum); |
意味着当您看到一个类型为
1 | (*alarm_handler)(-1); |
结果是
因此,如果我们声明:好的。
1 | extern SignalHandler alt_signal(void); |
这意味着:好的。
1 | (*alt_signal)(); |
表示空值。因此:好的。
1 | extern void (*alt_signal(void))(int signum); |
是等效的。现在,
1 2 3 | extern void (*signal(int signum, SignalHandler handler))(int signum); extern void (*signal(int signum, void (*handler)(int signum)))(int signum); |
如果这仍然让你困惑,我不知道该如何帮助你——它在某种程度上对我来说仍然是神秘的,但我已经习惯了它的工作方式,因此可以告诉你,如果你再坚持25年左右,它将成为你的第二天性(如果你聪明的话,甚至可能更快)。好的。好啊。
型
函数指针与任何其他指针类似,但它指向函数的地址,而不是数据的地址(在堆或堆栈上)。像任何指针一样,它需要正确地键入。函数由其返回值和接受的参数类型定义。因此,为了充分描述一个函数,必须包含它的返回值,并且接受每个参数的类型。当您对这样的定义进行typedef时,会给它一个"友好名称",这使得使用该定义创建和引用指针变得更加容易。
例如,假设你有一个函数:
1 2 | float doMultiplication (float num1, float num2 ) { return num1 * num2; } |
。
然后是以下typedef:
1 | typedef float(*pt2Func)(float, float); |
可用于指向此
因此,您可以创建一个指向domultiplication函数的指针,如下所示:
1 | pt2Func *myFnPtr = &doMultiplication; |
。
您可以使用以下指针调用函数:
1 | float result = (*myFnPtr)(2.0, 5.1); |
。
这是一个很好的读物:http://www.newty.de/fpt/index.html
型
一种很容易理解函数指针typedef的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | int add(int a, int b) { return (a+b); } typedef int (*add_integer)(int, int); //declaration of function pointer int main() { add_integer addition = add; //typedef assigns a new variable i.e."addition" to original function"add" int c = addition(11, 11); //calling function via new variable printf("%d",c); return 0; } |
型
至于使复杂声明更易于解析以便将来维护(由您自己或其他人)的技巧,我建议将
1 2 3 | typedef int (*FUNC_TYPE_1)(void); typedef double (*FUNC_TYPE_2)(void); typedef FUNC_TYPE_1 (*FUNC_TYPE_3)(FUNC_TYPE_2); |
而不是:
1 | typedef int (*(*FUNC_TYPE_3)(double (*)(void)))(void); |
号
1 2 3 4 5 6 | cdecl> explain int (*FUNC_TYPE_1)(void) declare FUNC_TYPE_1 as pointer to function (void) returning int cdecl> explain double (*FUNC_TYPE_2)(void) declare FUNC_TYPE_2 as pointer to function (void) returning double cdecl> declare FUNC_TYPE_3 as pointer to function (pointer to function (void) returning double) returning pointer to function (void) returning int int (*(*FUNC_TYPE_3)(double (*)(void )))(void ) |
事实上,这正是我在上面制造出的疯狂混乱。
型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | int add(int a, int b) { return (a+b); } int minus(int a, int b) { return (a-b); } typedef int (*math_func)(int, int); //declaration of function pointer int main() { math_func addition = add; //typedef assigns a new variable i.e."addition" to original function"add" math_func substract = minus; //typedef assigns a new variable i.e."substract" to original function"minus" int c = addition(11, 11); //calling function via new variable printf("%d ",c); c = substract(11, 5); //calling function via new variable printf("%d",c); return 0; } |
。
其输出为:
22
6
注意,在声明这两个函数时使用了相同的math-func定义符。
typedef的相同方法也可用于外部结构(在其他文件中使用struct)。
型
使用typedef定义更复杂的类型,即函数指针
我将以在C中定义状态机为例
1 | typedef int (*action_handler_t)(void *ctx, void *data); |
。
现在我们定义了一个名为action_handler的类型,它接受两个指针并返回一个int
定义状态机
1 2 3 4 5 6 7 8 | typedef struct { state_t curr_state; /* Enum for the Current state */ event_t event; /* Enum for the event */ state_t next_state; /* Enum for the next state */ action_handler_t event_handler; /* Function-pointer to the action */ }state_element; |
号
指向操作的函数指针看起来像一个简单类型,typedef主要用于此目的。
现在,我的所有事件处理程序都应遵循actionu handler定义的类型
1 2 3 | int handle_event_a(void *fsm_ctx, void *in_msg ); int handle_event_b(void *fsm_ctx, void *in_msg ); |
号
参考文献:
Linden的C编程专家
型
这是我作为练习编写的函数指针和函数指针数组的最简单示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |