C99:使用不同数量的参数转换回调

C99: cast callbacks with different number of arguments

在下面的示例中,我在指向应接收参数的函数的指针中创建了一个不带参数的函数的 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:[=27,使用生成的指针调用具有不兼容类型的函数不是=]

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

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

不兼容

For two function types to be compatible,… If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters,…

由于参数个数不一致,不兼容

上面只说了将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。这与问题无关,因为它不会使函数类型兼容,但这种声明格式在许多情况下为编译器提供了更多信息。)

对 Eric 的回答的补充想法,这也适用于 C99:

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

它可能会起作用,具体取决于您的编译器的 ABI。有些编译器让被调用函数清理堆栈,其他编译器让调用者清理。前者多半会崩溃,后者……谁知道呢

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

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

每个人都很开心:你和编译器。