c语言 - C99:使用不同数量的参数强制转换回调



在下面的示例中,我在指向应接收参数的函数的指针中制作了一个没有参数的函数的 CAST。假设它给出了预期的结果,此过程是否可能导致一些故障? 在线测试:https://onlinegdb.com/SJ6QzzOKI

typedef void (*Callback)(const char*);
Callback cb;
void inserisce_cb(void* c) {
cb=c;
}
void esegue_cb(){
cb("pippo");
}
void scriveTitolo(const char* titolo) {
Uart_Println(titolo);
}
void scriveTitolo2() {
Uart_Println("pluto");
}
void main(){
inserisce_cb(scriveTitolo);
esegue_cb();
inserisce_cb(scriveTitolo2);
esegue_cb();
}

将指向函数的指针转换为指向函数的另一个指针由 c 标准定义,但根据 C 6.3.2.3 8,使用生成的指针调用具有不兼容类型的函数则不然:

指向一种类型的函数的指针

可以转换为指向另一种类型的函数的指针,然后再转换回来;结果应与原始指针相等。如果使用转换后的指针来调用其类型与引用的类型不兼容的函数,则行为是未定义的。

声明void scriveTitolo2() { … }定义了一个没有参数类型列表的函数(它使用标识符列表的旧 C 样式,该列表为空),并且不带参数。Callback指针指向具有参数类型列表并采用const char *参数的函数。根据C 2018 6.7.6.3 15,这些是不兼容的:

要兼容两种函数类型,...如果一种类型具有参数类型列表,而另一种类型由包含(可能为空的)标识符列表的函数定义指定,则两者应在参数数量上达成一致,...

由于它们在参数数量上不一致,因此它们不兼容。

以上仅涉及从void (*)()转换为void (*){const char *)并使用结果调用函数的问题。还有一个单独的问题,即函数指针被传递给inserisce_cb,它接受类型为void *的参数,该参数是指向对象类型的指针。C 标准不定义将指向函数类型的指针转换为指向对象类型的指针的行为。为了解决这个问题,应该声明inserisce_cb接受指向函数类型的指针,例如void inserisce_cb(Callback c)

如果可以更改scriveTitolo2,则可以通过将其更改为采用未使用的const char *参数,将其定义更改为void scriveTitolo2(const char *)来解决兼容性问题。

(请注意,最好使用现代 C 样式声明scriveTitolo2,如void scriveTitolo2(void) { … },而不是没有void。这与问题无关,因为它不会使函数类型兼容,但这种声明格式在许多情况下为编译器提供了更多信息。

埃里克回答的其他想法,也适用于C99:

如果使用与函数的参数列表不兼容的参数列表调用函数,则这是根据 C99 §6.5.2.2 (6) 未定义的行为。

它可能会起作用,具体取决于编译器的 ABI。有些编译器让被调用的函数清理堆栈,有些编译器让调用方清理。前一种情况很可能会崩溃,后者...谁知道呢。

您可以使用忽略的参数声明scriveTitolo2

void scriveTitolo2(const char*) {
/* ... */
}

每个人都很高兴:你和编译器。

最新更新