C将void指针转换为函数指针

我正在尝试创建一些我可以用来创建自己的unit testing库的宏。 我的头文件如下所示:

#ifndef _TEST_H_ #define _TEST_H_ #include  #include "hehe_stack.h" static hehe_stack* tests; typedef int (*testfunc)(); #define test_init() tests = hehe_stack_init(); #define test_register(test) hehe_stack_push(tests, test); #define test_info() fprintf(stdout, "running %s :: %s \n", __FILE__, __func__); #define test_run() testfunc = (int (*)()) hehe_stack_pop(tests); testfunc(); return 0; #endif 

在每个测试.c文件中,我想将一些函数指针推入测试堆栈,然后将每个函数指针弹出堆栈并调用它。 我的堆栈pop方法返回一个void指针,我推送到它的函数指针返回一个int并且不带参数。 我的语法不正确吗? 我觉得我应该能够做到这一点。

C99标准不允许在指向数据的指针(标准,“对象或不完整类型”,例如char*void* )和指向函数的指针之间进行转换。

6.3.2.3:8指向一种类型函数的指针可以转换为指向另一种类型函数的指针,然后再返回; 结果应该等于原始指针。 如果转换的指针用于调用类型与指向类型不兼容的函数,则行为未定义。

一个原因是指向对象的指针和指向函数的指针不必具有相同的大小。 在示例架构上,前者可以是64位,后者可以是32位。

您可以从指向某个函数类型的指针转​​换为指向另一个函数类型的指针,如果您需要在数据结构中存储函数指针,这是我建议您使用的技术。 任何函数类型都可以。 这意味着您无法重用用于数据指针的数据结构。 您需要复制数据结构并将其更改为保存函数指针。

不要忘记在调用之前强制转换回正确的函数指针类型,否则这是未定义的行为。

请注意,正如Andrew Mellinger所指出的,一些编译器允许在每个方向进行转换。 C11的附件“J.5通用扩展” 列出 :

J.5.7函数指针强制转换

1指向对象或void的指针可以转换为指向函数的指针,允许将数据作为函数调用(6.5.4)。

2可以将指向函数的指针强制转换为指向对象的指针或使其无效,从而允许检查或修改函数(例如,通过调试器)(6.5.4)。

一些POSIX接口,例如dlsym() ,实际上也要求这些转换在POSIX系统的C编译器中有效。

你可以做到,但你不应该偶然做到这一点,所以语法特别尴尬。 一个不会转换函数指针,而是指向函数指针,然后指定它。

 #define test_run() *((void**)&testfunc) = hehe_stack_pop(tests); testfunc(); return 0; 

这将&testfunc转换为指向void*的指针,然后取消引用它并为其分配另一个void*的值,这是合法的。

建议的代码永远不会编译,因为你不应该取消引用void *指针(你怎么可能?没有关于特定指针的类型信息。)

cmotley在他的评论中建议的方式是正确的方法,虽然为了便于阅读我会建议一些改进:

 typedef int (*tTestFuncSignature)(void) #define test_run() tTestFuncSignature testfunc = hehe_stack_pop(tests); testfunc(); 

甚至避免使用此宏隐藏名称冲突:

 #define test_run() ((tTestFuncSignature)hehe_stack_pop(tests))(); 

无论哪种方式,您必须确保(例如通过合同)您只在堆栈中获得有效指针或必须在调用函数之前先测试指针。

编辑:更正的代码格式