将这2个函数强制转换为回调指针类型然后在不强制返回的情况下调用它们是否安全?
typedef void (*callback)(int i, ...);
void foo() {
// something.
}
void foo2(int i, int j) {
// something.
}
int main(void)
{
callback c = (callback)&foo, c2 = (callback)&foo2;
(*c)(0); (*c2)(0,1);
return 0;
}
将这2个函数强制转换为回调指针类型然后在不强制返回的情况下调用它们是否安全?
否。callback
从语言规范的“兼容”的意义上讲,这两个函数的类型与type不兼容,因此,通过类型的指针调用这两个函数中的任何一个都会callback
引起未定义的行为。总的来说,非可变函数类型永远不会与可变函数类型兼容,并且在实践中,许多实现对一种类型使用的调用约定与对另一种类型使用的调用约定不同,因此,甚至没有理由希望将一种函数调用为如果它是其他品种,则将以任何一致的方式产生所需的效果。
您有几种选择,其中包括:
为不同的目的使用不同的回调类型,每种类型都适合其预期的回调接口。这样,您可以完全避免转换回调函数。这是我的建议。它实现了最佳的类型安全性,并且您需要以某种方式始终跟踪实际的回调类型是什么,以便可以正确调用它。
使用函数指针类型的并集。回调说明符分配给联合的适当成员,而回调呼叫者选择适当的成员。
typedef union {
int (*unary)(int i);
int (*binary)(int i, int j);
} callback;
// ...
callback cb1 = { .unary = foo };
callback cb2 = { .binary = foo2 };
cb1.unary(1);
cb2.binary(1, 2);
您甚至可以使用带标签的联合,该联合还附带有关使用哪个成员的信息。使用起来会有些复杂,但是它将为您提供一种实现附加类型安全性的方法。如果您需要一种可以传递多种回调类型的数据类型,则我的后备建议就是这种方法的一种变体。
选择一种满足您所有需求的回调类型。一种实现方法是为它提供一个type参数void *
,通过该参数回调函数可以接受任何数量和类型的输入,例如,指向合适结构类型的指针。
typedef int (*callback)(void *);
struct one_int { int i1; };
struct two_int { int i1, i2; };
int foo(void *args) {
struct one_int *one_int = args; // ...
}
int foo2(void *args) {
struct two_int *two_int = args; // ...
}
选择任何功能类型作为callback
。强制转换为该类型,然后返回到原始类型以进行呼叫。
指定没有原型的回调类型。在C语言中,如果不属于该函数定义的函数声明未指定参数类型列表,则意味着没有提供有关参数的信息(与C ++不同,在C ++中,这意味着该函数没有参数) 。这与需要任何特定数量的参数(但不可变参数)的函数兼容,只要将默认参数提升应用于参数类型即可产生兼容的类型。int
在这方面,类型是一种精细的参数类型。可能会出现问题的主要类型是小于int
,plus的整数类型float
。
typedef int (*callback)();
这将完全允许您为示例中的特定功能类型描述用法。
callback cb1 = foo;
callback cb2 = foo2;
(*cb1)(1); // or just cb1(1)
(*cb2)(2); // or just cb2(1, 2)
与另一个答案的主张相反,对该方法的支持并不构成对迄今为止已发布的C语言规范的任何版本的扩展。支持它是符合C89,C99,C11和C17中任何一个的要求。但是,它已在C17中声明为“过时”,这构成了警告,可能会将其从语言规范的某些将来版本中删除。我希望确实可以删除它,可能会在下一个规范版本中将其删除,尽管过时并不能保证一定会删除。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句