我使用函数指针的结构来实现不同后端的接口。签名非常不同,但返回值几乎都是void、void *或int。
struct my_interface {
void (*func_a)(int i);
void *(*func_b)(const char *bla);
...
int (*func_z)(char foo);
};
但不要求后端对每个接口函数都支持函数。所以我有两种可能,第一种选择是在每次调用之前检查指针是否为空。我不太喜欢这一点,因为它的可读性,也因为我担心它会影响性能(不过,我还没有测量过它)。另一种选择是有一个虚拟函数,在极少数情况下,接口函数不存在。
因此,我需要每个签名都有一个伪函数,我想知道是否有可能只有一个用于不同的返回值。并将其转换为给定的签名。
#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一起测试了这段代码,它可以正常工作。但这是合理的吗?或者它会破坏堆栈,或者会导致其他问题吗?
编辑:感谢所有的答案,在进一步阅读之后,我现在学到了很多关于调用约定的知识。现在对引擎盖下发生的事情有了更好的理解。
发布于 2008-10-09 19:48:34
我怀疑你会得到一个不确定的行为。
您可以(通过适当的转换)将函数的指针分配给另一个具有不同签名的函数的指针,但当您调用它时,可能会发生奇怪的事情。
你的nothing()
函数没有参数,对于编译器来说,这可能意味着他可以优化堆栈的使用,因为那里将没有参数。但在这里,你用一个参数来调用它,这是一个意想不到的情况,它可能会崩溃。
我在标准中找不到合适的地方,但我记得它说你可以强制转换函数指针,但当你调用结果函数时,你必须使用正确的原型,否则行为是未定义的。
作为附注,您不应该将函数指针与数据指针(如NULL)进行比较,因为这两个指针可能属于不同的地址空间。C99标准中有一个附录,允许这种特定情况,但我不认为它得到了广泛的实现。也就是说,在只有一个地址空间的体系结构上,将函数指针转换为数据指针或将其与NULL进行比较,通常是可行的。
发布于 2008-10-09 19:39:07
您确实面临着导致堆栈损坏的风险。话虽如此,如果您使用extern "C"
链接(和/或__cdecl
,取决于您的编译器)来声明函数,那么您也许能够摆脱这一点。这将类似于像printf()
这样的函数可以根据调用者的判断接受可变数量的参数。
这在您当前的情况下是否有效可能还取决于您正在使用的确切编译器选项。如果您使用的是MSVC,那么debug和release编译选项可能会有很大的不同。
发布于 2008-10-09 19:37:13
应该没问题。因为调用者负责在调用后清理堆栈,所以它不应该在堆栈上留下任何额外的东西。被调用者(在本例中为nothing())没有问题,因为它不会尝试使用堆栈上的任何参数。
EDIT:这确实假设了cdecl调用约定,这通常是C的默认调用约定。
https://stackoverflow.com/questions/188839
复制相似问题