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

时间:2012-12-04 05:53:17

标签: c

我正在尝试创建一些可用于创建自己的单元测试库的宏。我的头文件如下所示:

#ifndef _TEST_H_
#define _TEST_H_

#include <stdio.h>
#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并且不带参数。我的语法不正确吗?我觉得我应该能够做到这一点。

3 个答案:

答案 0 :(得分:25)

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

  

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

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

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

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

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

J.5.7函数指针强制转换

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

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

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

答案 1 :(得分:6)

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

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

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

答案 2 :(得分:0)

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

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

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

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

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

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

编辑:更正代码格式