函数指针转换为不同的签名 [英] Function pointer cast to different signature

查看:19
本文介绍了函数指针转换为不同的签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用函数指针的结构来实现不同后端的接口.签名很不一样,但返回值几乎都是void、void *或int.

I use a structure of function pointers to implement an interface for different backends. The signatures are very different, but the return values are almost all void, void * or int.


struct my_interface {
    void  (*func_a)(int i);
    void *(*func_b)(const char *bla);
    ...
    int   (*func_z)(char foo);
};

但并不要求后端支持每个接口函数的函数.所以我有两种可能性,第一种选择是在每次调用之前检查指针是否不等于 NULL.我不太喜欢那样,因为它的可读性和我担心性能影响(但是我还没有测量它).另一种选择是有一个虚拟函数,在极少数情况下接口函数不存在.

But it is not required that a backends supports functions for every interface function. So I have two possibilities, first option is to check before every call if the pointer is unequal NULL. I don't like that very much, because of the readability and because I fear the performance impacts (I haven't measured it, however). The other option is to have a dummy function, for the rare cases an interface function doesn't exist.

因此,我需要为每个签名创建一个虚拟函数,我想知道是否有可能只有一个用于不同的返回值.并将其转换为给定的签名.

Therefore I'd need a dummy function for every signature, I wonder if it is possible to have only one for the different return values. And cast it to the given signature.


#include <stdio.h>

int nothing(void) {return 0;}

typedef int (*cb_t)(int);

int main(void)
{
    cb_t func;
    int i;

    func = (cb_t) nothing;
    i = func(1);

    printf("%d
", i);

    return 0;
}

我用 gcc 测试了这段代码,它工作正常.但它是理智的吗?或者它会破坏堆栈还是会导致其他问题?

I tested this code with gcc and it works. But is it sane? Or can it corrupt the stack or can it cause other problems?

感谢所有的答案,经过一些进一步的阅读,我现在学到了很多关于调用约定的知识.现在对幕后发生的事情有了更好的了解.

Thanks to all the answers, I learned now much about calling conventions, after a bit of further reading. And have now a much better understanding of what happens under the hood.

推荐答案

根据 C 规范,强制转换函数指针会导致未定义的行为.事实上,有一段时间,GCC 4.3 预发布版本会在您强制转换函数指针时返回 NULL,规范完全有效,但他们在发布前取消了该更改,因为它破坏了很多程序.

By the C specification, casting a function pointer results in undefined behavior. In fact, for a while, GCC 4.3 prereleases would return NULL whenever you casted a function pointer, perfectly valid by the spec, but they backed out that change before release because it broke lots of programs.

假设 GCC 继续做它现在所做的事情,它将在默认的 x86 调用约定(以及大多数架构上的大多数调用约定)下正常工作,但我不会依赖它.在每个调用站点针对 NULL 测试函数指针并不比函数调用昂贵多少.如果你真的想要,你可以写一个宏:

Assuming GCC continues doing what it does now, it will work fine with the default x86 calling convention (and most calling conventions on most architectures), but I wouldn't depend on it. Testing the function pointer against NULL at every callsite isn't much more expensive than a function call. If you really want, you may write a macro:

#define CALL_MAYBE(func, args...) do {if (func) (func)(## args);} while (0)

或者您可以为每个签名使用不同的虚拟函数,但我可以理解您希望避免这种情况.

Or you could have a different dummy function for every signature, but I can understand that you'd like to avoid that.

Charles Bailey 为此向我提出了意见,所以我去查看了详细信息(而不是依靠我糟糕的记忆).C 规范

Charles Bailey called me out on this, so I went and looked up the details (instead of relying on my holey memory). The C specification says

766 指向一种类型函数的指针可以转换为指向另一种类型函数的指针,然后再返回;
767 结果应与原始指针相等.
768 如果使用转换后的指针调用类型与指向类型不兼容的函数,则行为未定义.

766 A pointer to a function of one type may be converted to a pointer to a function of another type and back again;
767 the result shall compare equal to the original pointer.
768 If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.

和 GCC 4.2 预发布版(这在 4.3 之前已解决)遵循以下规则:函数指针的强制转换不会导致 NULL,正如我所写的,而是尝试通过不兼容的类型调用函数,即

and GCC 4.2 prereleases (this was settled way before 4.3) was following these rules: the cast of a function pointer did not result in NULL, as I wrote, but attempting to call a function through a incompatible type, i.e.

func = (cb_t)nothing;
func(1);

根据您的示例,将导致 abort.他们改回 4.1 的行为(允许但警告),部分原因是此更改破坏了 OpenSSL,但同时 OpenSSL 已得到修复,这是编译器可以随时自由更改的未定义行为.

from your example, would result in an abort. They changed back to the 4.1 behavior (allow but warn), partly because this change broke OpenSSL, but OpenSSL has been fixed in the meantime, and this is undefined behavior which the compiler is free to change at any time.

OpenSSL 只是将函数指针转换为指向其他函数类型的函数指针,这些函数类型采用和返回相同数量的相同大小的值,并且这(假设您不处理浮点数)在所有平台上都是安全的,并且我知道的调用约定.但是,其他任何东西都可能不安全.

OpenSSL was only casting functions pointers to other function types taking and returning the same number of values of the same exact sizes, and this (assuming you're not dealing with floating-point) happens to be safe across all the platforms and calling conventions I know of. However, anything else is potentially unsafe.

这篇关于函数指针转换为不同的签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆