【问题标题】:C function pointer cast to another function pointerC函数指针转换为另一个函数指针
【发布时间】:2015-06-11 15:36:19
【问题描述】:

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

我有两种函数指针类型:

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_func1_t 转换为 draw_func2_t 的函数:

draw_func2_t convert_func_p(draw_func1_t func) { ... }

我该怎么写?我可以强制像这样的演员吗

return (draw_func2_t)func;

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

【问题讨论】:

  • 实际上想要完成什么?我从未见过需要在函数指针类型之间进行转换。这样做通常是不安全的。
  • 您写了 我需要编写一个将 draw_func1_t 转换为 draw_func2_t 的函数,但您的示例进行了相反的转换。实际需要什么?
  • 最便携的选项是只使用draw_func1_t 来实现这两个功能。你仍然可以合法地将MyPainter * 传递给void * 参数,只是不会得到你想要的类型检查。也许您可以在 MyPainter 结构中添加一个幻数并在运行时检查它。
  • @WernerHenze 他在询问是否将一个函数指针转换为另一个函数指针(大小相同)。我不确定您链接到该问题的目的是什么。

标签: c function pointers function-pointers


【解决方案1】:

如果你将函数指针转换为不同的类型,那么它的调用行为是未定义的。见C标准附录J.2:

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

兼容性在 6.7.5.1 第 2 段中处理:

为了使两种指针类型兼容,两者应相同 限定并且两者都应该是指向兼容类型的指针。

MyPainter*void* 不兼容。所以你的函数指针转换不能用于调用函数。

【讨论】:

  • 演员阵容有效。使用强制转换指针不是。也就是说,您可以安全地将指向函数的指针转换为指向不同函数类型的指针;如果您随后将其转换回原始类型,您将保留该值。这相当于往返将指向对象类型的指针转​​换为 void* 并返回。但是您不能像引用中那样使用强制转换指针调用该函数。
  • “那么它的调用行为是未定义的”是不正确的。它是完美定义的,也就是说,它会按照我说的去做(这就是我们有演员表的原因)。只有当这些函数的调用约定不同时,行为才会不确定。
  • 如果void *painter实际上是MyPainter数据结构的抽象隐藏,那么两者是内部兼容的。没有 C 语言结构禁止强制转换。
  • 附录 J 是非规范性的,尽管在涵盖它的标准主体中有规范性文本。
  • @Paul Ogilvie:如上所述,演员阵容本身并没有被禁止。但是使用强制转换的结果来执行函数调用会导致未定义的行为。 C 语言非常明确地说明了这一点。正如this answer正确指出的那样,这种情况下的函数类型不兼容。
【解决方案2】:

自从使用:

draw_func1_t convert_func_p(draw_func2_t func)
{
   return (draw_func1_t)func;
}

导致未定义的行为,您可能需要更改策略。

假设你有:

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_wrapper作为回调。

您还可以通过删除对MyPainter* 的显式强制转换来简化func2_wrapper

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

【讨论】:

  • 这很优雅,但是它需要 func2 存在。不幸的是 func2 是由库的用户提供的,我必须使用函数指针。
  • @SherwoodHu,没关系。您需要创建func2_wrapper,而不是func2
  • @RSahu:OP 说“func2 由库的用户提供”,我认为这意味着您的代码在编译时不知道 func2。
  • @newacct,感谢您的澄清。由于某种原因,我误读了。
  • 没有多余的演员表会更好。 void * 可以隐式转换为其他对象指针类型。事实上,你可以去func2(data, painter, x, y); 并完全摆脱realPainter
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    下面的编译没有警告(VC2008)并且表明这两种函数类型是兼容的。出乎意料的是,void * 在需要 MyPainter * 的情况下被接受。

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

    【讨论】:

      猜你喜欢
      • 2011-07-31
      • 2012-11-21
      • 2016-06-03
      • 2012-01-25
      • 1970-01-01
      • 2013-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多