【问题标题】:Conversion between function pointers with void and non-void pointer parameters带 void 和非 void 指针参数的函数指针之间的转换
【发布时间】:2014-09-05 14:35:09
【问题描述】:

我有一个与 void 指针转换有关的问题。我的问题不是在 void* 和非 void 指针之间进行转换,而是关于函数指针类型之间的转换,其中一个具有 void* 作为参数类型,另一个具有指向某些特定数据类型的指针。

这是允许重现警告消息的代码:

#include <stdio.h>

typedef void (*module_outputMessage)(void *param, const char *msg);

void module_function(module_outputMessage outputFunc, void *output_param, int msgid, const char *msg1, const char *msg2)
{
    if (msgid == 0)
        outputFunc(output_param, msg1);
    else
        outputFunc(output_param, msg2);
}



struct main_state
{
    int msgid;
};

void main_outputMessage(struct main_state *state, const char *str)
{
    printf("Message %d: %s\n", state->msgid, str);
    state->msgid++;
}

int main(int argc, char *argv[])
{
    struct main_state state;
    const char *msg1 = "abc", *msg2 = "def";

    state.msgid = 0;

    module_function(&main_outputMessage, &state, 0, msg1, msg2);
    module_function(&main_outputMessage, &state, 0, msg1, msg2);
    module_function(&main_outputMessage, &state, 1, msg1, msg2);
    module_function(&main_outputMessage, &state, 0, msg1, msg2);
    module_function(&main_outputMessage, &state, 1, msg1, msg2);

    return 0;
}

就是这样,程序由main和module两部分组成。 module 输出文本,但它不应该处理所有输出细节 - 相反,main 是处理输出的那个。由于main 依赖于module 而不是相反,module 不知道main 中发生了什么,为了输出消息,它需要一个输出函数作为参数传递。为了让输出知道它正在处理哪个状态对象,该对象需要与输出函数一起传递。这就是转换发挥作用的地方:module 不知道也不应该关心main 的实现,因此它不使用struct main_state* 作为函数参数,而是接受void*,它只是传递给输出函数。

所以这一切都归结为这些类型之间的转换:

void (*)(void*              , const char*)
void (*)(struct main_state *, const char*)

程序给出了预期的结果:

Message 0: abc
Message 1: abc
Message 2: def
Message 3: abc
Message 4: def

但是,GCC 抱怨指针类型不兼容(我收到五条这样的消息,每个函数调用一条):

funcpointvoid.c: In function ‘main’:
funcpointvoid.c:33:2: warning: passing argument 1 of ‘module_function’ from incompatible pointer type
  module_function(&main_outputMessage, &state, 0, msg1, msg2);
  ^
funcpointvoid.c:5:6: note: expected ‘module_outputMessage’ but argument is of type ‘void (*)(struct main_state *, const char *)’
 void module_function(module_outputMessage outputFunc, void *output_param, int msgid, const char *msg1, const char *msg2)
      ^

因此,即使它对我来说效果很好,但有了这些警告,我不确定是否可以依赖这种“架构”。但正如我自己所看到的,唯一的区别是指向 void 和 non-void 的指针,这只是将泛型指针用于它们存在的任何目的的一种方式。这是 GCC 的错误还是我错过了什么?

【问题讨论】:

  • 也许最简单的方法是将main_outputMessage原型更改为void (void *, const char*),然后将void*参数转换为struct main_state*。
  • 一个更短的代码,将重现相同的警告/错误,将int (*pfn)(Type const*, Type const*) 传递给标准库函数qsort 以对Type 序列进行排序。我还没有遇到一个工程师,在最初使用学习该 api 时,没有遇到同样的情况。
  • @Holt 这不仅是解决此问题的最简单方法,而且是正确的方法。+1.

标签: c gcc function-pointers void-pointers


【解决方案1】:
void (*)(void*              , const char*)
void (*)(struct main_state *, const char*)

是两种不同的类型,由于函数指针类型之间没有隐式转换,您需要使用强制转换来显式转换:

变化:

module_function(&main_outputMessage, &state, 0, msg1, msg2);

到

module_function((module_outputMessage) main_outputMessage,
    &state, 0, msg1, msg2);

但请注意,函数调用 outputFunc 在技术上会调用未定义的行为,因为 void (*)(void* , const char*) 和 void (*)(struct main_state *, const char*) 不是兼容的类型。

【讨论】:

  • 我不确定这是否适用于指向不同类型的指针大小不同的架构? C 标准没有指定任何内容,但这不是常见的情况。
  • 嗯,我想我得到了一些东西,但不是全部。如果像@Holt 建议的那样,struct main_state* 被转换为void*,使用函数调用传递然后又转换回struct main_state*,并且没有完成不同函数指针之间的转换,那么就没有未定义的行为,是吗?
  • @CrystalMV 未定义的行为来自outputFunc 函数调用,正如 C 所说 (c99, 6.3.2.3p8) “如果使用转换后的指针调用类型不是与指向的类型兼容,行为未定义。” 而且这两种类型不兼容(即使void * 和struct main_state * 是“赋值兼容”)。
  • @CrystalMV: main() 将struct main_state 转换为void * 并将其传递给module_function()。 module_function() 将void * 传递给main_outputMessage()(通过outputFunc())。到目前为止,一切都很好。问题是main_outputMessage()实际上认为它已经通过了struct main_state。没有从void * 转换回来。
猜你喜欢
  • 1970-01-01
  • 2021-05-09
  • 2019-04-13
  • 2011-07-31
  • 1970-01-01
  • 2013-05-21
  • 2020-06-11
  • 2012-11-21
  • 1970-01-01
相关资源
最近更新 更多