C函数指针强制转换为另一个函数指针

C函数指针强制转换为另一个函数指针,c,function,pointers,function-pointers,C,Function,Pointers,Function Pointers,我需要函数指针方面的帮助 我有两种函数指针类型: typedef void (*draw_func1_t)(void* data, void* painter, double x, double y); typedef void (*draw_func2_t)(void* data, MyPainter* painter, double x, double y); 除了第二个参数外,这两种类型几乎相同。现在我需要编写一个函数,将draw\u func1\u t转换为draw\u func2\t

我需要函数指针方面的帮助

我有两种函数指针类型:

typedef void (*draw_func1_t)(void* data, void* painter, double x, double y);
typedef void (*draw_func2_t)(void* data, MyPainter* painter, double x, double y);
除了第二个参数外,这两种类型几乎相同。现在我需要编写一个函数,将draw\u func1\u t转换为draw\u func2\t:

draw_func2_t convert_func_p(draw_func1_t func) { ... }
我怎么写呢?我可以强制一个像这样的演员吗

return (draw_func2_t)func;

因为这两个函数原型是二进制兼容的?

如果您将函数指针强制转换为不同的类型,则其调用行为是未定义的。见C标准附录J.2:

在以下情况下,行为未定义:指针 用于调用类型与不兼容的函数 指向类型(6.3.2.3)

兼容性在6.7.5.1第2段中讨论:

对于要兼容的两种指针类型,两者应相同 合格,且两者都应是指向兼容类型的指针

MyPainter*
void*
不兼容。因此,函数指针强制转换不能用于调用函数。

因为使用了:

draw_func1_t convert_func_p(draw_func2_t func)
{
   return (draw_func1_t)func;
}
void func2_wrapper(void* data, void* painter, double x, double y)
{
   // In this function, if you are sure that painter points to
   // a valid MyPainter object, you can do this:
   MyPainter* realPainter = (MyPainter*)painter;

   // Then call the core function.
   func2(data, realPainter, x, y);
}
导致未定义的行为,您可能需要更改策略

假设你有:

void func2(void* data, MyPainter* painter, double x, double y)
{
   printf("In func2, working with MyPainter\n");
}
您希望能够通过函数指针间接使用该函数

一种选择是使用包装器函数

void func2_wrapper(void* data, void* painter, double x, double y)
{
   // In this function, if you are sure that painter points to
   // a valid MyPainter object, you can do this:
   MyPainter* realPainter = (MyPainter*)painter;

   // Then call the core function.
   func2(data, realPainter, x, y);
}
func2\u包装器注册为回调

通过删除对
MyPainter*
的显式转换,还可以简化
func2\u包装程序

void func2_wrapper(void* data, void* painter, double x, double y)
{
   func2(data, painter, x, y);
}

从理论上讲,如果您将其转换为不同的签名并调用它,这是未定义的行为,正如Bathsheba的回答所引用的,因为调用不同类型函数的调用约定可能不同


然而,实际上,它几乎可以在任何实际系统上工作,因为几乎所有的调用约定都以相同的方式处理不同类型的(非函数)指针。因为这是唯一的区别(所有其他的东西,包括参数的数量和返回类型都是相同的),所以调用约定几乎肯定是相同的。您可以检查特定系统调用ABI的函数,以确保。

以下编译没有警告(VC2008),并显示这两种函数类型是兼容的。出乎意料的是,如果需要
MyPainter*
,则接受
void*

typedef struct {
    int x;
    int y;
} MyPainter;

typedef void (*draw_func1_t)(void* data, void* painter, double x, double y);
typedef void (*draw_func2_t)(void* data, MyPainter* painter, double x, double y);

void f1(void* data, void* painter, double x, double y);
void f2(void* data, MyPainter* painter, double x, double y);

void f1(void* data, void* painter, double x, double y)
{
    f2(data,painter,x,y);   // no compiler warning is unexpected
}
void f2(void* data, MyPainter* painter, double x, double y)
{
    f1(data,painter,x,y);   // no compiler warning is expected
}
void pTest(void)
{
    MyPainter p = {0,0};
    draw_func1_t pf1;
    draw_func2_t pf2;

    pf1= f1;
    pf2= f1;

    pf1= f2;
    pf2= f2;

    pf1(0,&p,0.0,0.0);
    pf2(0,&p,0.0,0.0);
}

你到底想完成什么?我从未见过需要在函数指针类型之间转换。这样做通常是不安全的。您编写了我需要编写一个函数,将
draw\u func1\u t
转换为
draw\u func2\t
,但您的示例进行了相反的转换。实际需要什么?最可移植的选项是仅对这两个函数使用
draw\u func1\t
。您仍然可以合法地将
MyPainter*
传递到
void*
参数中,这样您就不会得到您想要的那么多类型检查。也许您可以在MyPainter结构中添加一个神奇的数字,并在运行时检查它。@WernerHenze他问的是如何将一个函数指针转换为另一个函数指针(大小相同)。我不确定你的观点是什么。演员阵容是有效的。不允许使用强制转换指针。也就是说,您可以安全地将指向函数的指针强制转换为指向不同函数类型的指针;如果随后将其强制转换回原始类型,则将保留该值。这相当于来回将指向对象类型的指针强制转换为void*并返回。但是您不能使用cast指针调用函数,如您的引用中所述。“那么调用函数的行为是未定义的”是不正确的。它是完美定义的,也就是说,它将按照我告诉它的去做(这就是为什么我们有演员)。只有当这些函数的调用约定不同时,行为才会被取消定义。如果
void*painter
实际上是
MyPainter
数据结构的抽象隐藏,那么这两者在内部是兼容的。没有语言结构禁止铸造。附录J是非规范性的,尽管标准主体中包含规范性文本。@Paul Ogilvie:如上所述,铸造本身是不被禁止的。但是使用强制转换的结果执行函数调用会导致未定义的行为。C语言非常明确地说明了这一点。本例中的函数类型不兼容,因为此答案正确说明了这一点。这很优雅,但它要求func2存在。不幸的是func2是由库的用户提供的,我必须使用函数指针。@SherwoodHu,没关系。您需要创建
func2\u包装
,而不是
func2
@RSahu:OP说“func2由库的用户提供”,我认为这意味着您的代码在编译时不知道func2。@newacct,感谢您的澄清。出于某种原因,我误解了这一点。如果没有多余的演员阵容,这会更好<代码>无效*
可以隐式转换为其他对象指针类型。事实上,您可以使用
func2(数据、画师、x、y)
并完全摆脱
realPainter