c - 转换为不同签名的函数指针

标签 c pointers

我使用一个函数指针结构来为不同的后端实现一个接口(interface)。签名差别很大,但返回值几乎都是void、void *或int。


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

但并不要求后端支持每个接口(interface)函数的函数。所以我有两种可能性,第一种选择是在每次调用之前检查指针是否不等于 NULL。我不太喜欢这样,因为可读性差,而且我担心性能会受到影响(不过我还没有对其进行测量)。另一种选择是使用虚拟函数,在极少数情况下接口(interface)函数不存在。

因此我需要为每个签名设置一个虚拟函数,我想知道是否可以只为不同的返回值设置一个虚拟函数。并将其转换为给定的签名。


#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\n", i);

    return 0;
}

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

编辑:多亏了所有的答案,在进一步阅读之后,我现在学到了很多关于调用约定的知识。现在对引擎盖下发生的事情有了更好的理解。

最佳答案

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

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

#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.

Edit

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 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.

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);

在您的示例中,将导致中止。他们改回 4.1 行为(允许但警告),部分原因是此更改破坏了 OpenSSL,但 OpenSSL 已在此期间得到修复,这是编译器可以随时更改的未定义行为。

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

关于c - 转换为不同签名的函数指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/188839/

相关文章:

c - 你如何在 C 中创建一个指针数组?

pointers - 将指针的值存储在 Rust 中的另一个指针中

c - 如何修复 scanf? (故障保护)

c - 分段故障

c - 如何确定文件是否包含类似 UTF-8 的字符

c - 传递指针的free() 'know'如何有效?

java - 请帮我提供 self 引用指针

c - 子目录中的头文件(例如 gtk/gtk.h 与 gtk-2.0/gtk/gtk.h)

c - 使用strtok在c中分割一个字符串

java - 交换方法在 C# 中如何在内存级别工作?