将函数转换为函数指针

时间:2014-08-28 03:47:11

标签: c

typedef void (*LoopCallback)(int fd, void * arg);

LoopCallback func_ptr = 0;

void call_back(int value)
{
    printf("%d", value);
}

void sys_register_input(LoopCallback call_back)
{
    func_ptr = call_back;
}

int main()
{
    sys_register_input((LoopCallback)call_back);
    func_ptr(33, 0);
}

在其中一个遗留项目中找到此代码,这种转换是否在c?

中有效

编辑:调用函数ptr

2 个答案:

答案 0 :(得分:5)

不,它无效。调用者期望的原型与被调用函数的原型不匹配,因此可能导致未定义的行为。具体结果将取决于编译器和调用约定等因素(被调用的函数可能最终只会看到arg中的垃圾,或者线程堆栈可能会被破坏而导致崩溃)。

答案 1 :(得分:3)

安德鲁医学是正确的,当时他说这是无效的,但在实际操作中,肯定会对绝大多数现有的ABI起作用,并且可靠地工作。

今天使用的大多数ABI让调用者在寄存器中执行所有堆栈维护和/或传递参数,因此如果给函数提供的参数多于它所要求的参数,那么没有人会注意到。您只需要一些未使用的堆栈空间,或者将值写入从未使用过的寄存器。

实际上,你也经常可以反过来;也就是说,为函数提供的参数少于它所要求的参数。它将接收这些参数的未定义值,但如果它不使用它们你将没有问题。事实上,标准的POSIX C函数open()取决于这种行为,这表明C ABI现在和历史上对这些事情的窒息是多么罕见。

如果可能的话,你当然应该避免依赖这种行为,但特别是如果你不关心过度可移植我不会真的看到一个不理会的理由。然而, 应该非常清楚你正在做什么,如果你不觉得自己精通原因,就不应该使用这些技巧他们可能会或可能不会工作。

如果您在类似Pascal / BASIC的ABI(如Windows上的stdcall)中编写函数,那么调用者会设置堆栈但被调用者将其清理干净。在这种情况下,调用者会推送两个参数,但被调用者只弹出一个参数,因此返回后堆栈指针与其预调用值不匹配,这会引起各种奇怪,包括但不限于调用函数当它返回时,不使用正确的返回地址。崩溃将得到保证。然而,这在C中是非常不寻常的事情。