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*) {
/* ... */
}
每个人都很开心:你和编译器。
在下面的示例中,我在指向应接收参数的函数的指针中创建了一个不带参数的函数的 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*) {
/* ... */
}
每个人都很开心:你和编译器。