转换具有不同返回类型的函数指针

Tha*_*gon 2 c

这个问题是关于使用函数指针,它不完全兼容,但我希望我可以使用它,只要我的代码只依赖于兼容的部分。让我们从一些代码开始来了解这个想法:

typedef void (*funcp)(int* val);

static void myFuncA(int* val) {
  *val *= 2;
  return;
}

static int myFuncB(int* val) {
  *val *= 2;
  return *val;
}

int main(void) {
  funcp f = NULL;
  int v = 2;

  f = myFuncA;
  f(&v);
  // now v is 4

  // explicit cast so the compiler will not complain
  f = (funcp)myFuncB;
  f(&v);
  // now v is 8

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

虽然参数myFuncAmyFuncB相同且完全兼容,返回值不并因此仅仅通过调用代码忽略。我尝试了上面的代码,它使用 GCC 可以正常工作。

到目前为止,我从这里这里学到的是,根据标准的定义,这些函数是不兼容的,可能会导致未定义的行为。然而,我的直觉告诉我,我的代码示例仍然可以正常工作,因为它不以任何方式依赖于不兼容的部分(返回值)。然而,在这个问题的答案中已经提到了堆栈的可能损坏。

所以我的问题是:我上面的例子是有效的 C 代码,所以它总是按预期工作(没有任何副作用),还是依赖于编译器?


编辑:我想这样做是为了使用具有“功能强大”界面的“更强大”功能​​。在我的例子中funcp是接口,但我想提供额外的功能,比如myFuncB可选使用。

Gem*_*lor 5

同意这是未定义的行为,不要那样做!

是的代码功能,即它不会倒下,但您在返回 void 后分配的值是未定义的。

在一个非常旧的“C”版本中,返回类型是未指定的,int 和 void 函数可以“安全地”混合使用。在指定的累加器寄存器中返回的整数值。我记得使用这个“功能”编写代码!

对于您可能返回的几乎任何其他内容,结果很可能是致命的。

往后几年,浮点返回值经常使用 fp 协处理器(我们还在 80 年代)寄存器返回,所以不能混合 int 和 float 返回类型,因为如果协处理器的状态会混淆调用者不会剥离该值,或者剥离从未放置在那里的值并导致 fp 异常。更糟糕的是,如果您使用 fp 仿真构建,那么 fp 值可能会在堆栈上返回,如下所述。同样在 32 位构建上,可以传递 64 位对象(在 16 位构建上,您可以拥有 32 位对象),这些对象将使用多个寄存器或在堆栈上返回。如果它们在堆栈上并分配了错误的大小,则会发生一些局部踩踏,

现在,c 支持结构返回类型和返回值复制优化。如果您没有正确匹配类型,则所有赌注都将取消。

还有一些函数模型让调用者为调用的参数分配堆栈空间,但函数本身释放堆栈。调用者和实现之间在参数和返回值的数量或类型上的分歧将是致命的。

默认情况下,C 函数名被导出和链接未修饰——只是函数名定义了符号,所以程序的不同模块可能对函数签名有不同的看法,当你链接时会发生冲突,并可能产生非常有趣的运行时错误。

在 c++ 中,函数名被高度修饰主要是为了允许重载,但也有助于避免签名不匹配。这有助于保持参数同步,但实际上(如@Jens 所述)返回类型未编码到修饰名称中,主要是因为未使用返回类型(未使用,但我认为现在偶尔会影响) 用于重载解析。