嘿,我已经在我的C程序中实现了一些回调。
typedef void (*server_end_callback_t)(void *callbackArg);
然后我有变量内部结构来存储这个回调
server->server_end_callback = on_sever_end;
我注意到我可以传递on_server_end
回调函数实现,跳过void *callbackArg
并且代码正常工作(没有错误)。
跳过一些参数如void *
实现回调函数原型采用这样的参数是否正确?
void on_server_end(void) {
// some code goes here
}
答案 0 :(得分:2)
我认为从C的角度来看它是一种未定义的行为,但它的工作原理是因为你正在使用的调用约定。
例如,AMD64 ABI声明前六个参数使用CPU寄存器传递给调用函数,而不是堆栈。因此,调用者和被调用者都不需要对前六个参数进行清理,并且工作正常。
有关详细信息,请参阅the Wikipedia.
答案 1 :(得分:0)
由于传递参数的惯例,代码可以正常工作。调用者知道被调用者期望一些参数 - 恰好一个。因此,它准备参数(在寄存器或堆栈中 - 取决于您平台上的ABI)。然后被调用者是否使用这些参数。从被调用者返回后,调用者会在必要时清理堆栈。那是个谜。
但是,您不应通过传递不兼容的函数来滥用此特定行为。总是使用选项-W -Wall -Werror
(clang / gcc和compatible)编译代码是一种很好的做法。启用此选项会为您提供编译错误。
答案 2 :(得分:0)
C允许使用函数参数快速和松散地进行一定数量的播放。所以
void (*fptr) ();
表示“指向带有零个或多个参数的函数的指针”。然而,这是为了向后兼容,在新的C代码中使用它并不明智。另一种方式
void (*fptr)(void *ptr)
{
/* don't use the void */
}
/* in another scope */
(*fptr)(); /* call with no arguments */
也可以,只要你不使用void *,我相信它可以保证工作虽然我不完全确定(在现代机器上,调用约定是传递第一个参数in寄存器,所以你只需要一个垃圾寄存器,它会工作)。再一次,依靠它是一个非常糟糕的主意。
您可以传递一个void *,然后将其转换为适当类型的结构,其中包含任意数量的参数。这是一个好主意,也是对C灵活性的合理使用。
答案 3 :(得分:0)
跳过一些像void *实现回调函数的参数是正确的,原型采用这样的参数吗?
不,不是。具有给定函数声明的任何函数都不与不同函数声明的函数兼容。此规则也适用于指向函数的指针。
因此,如果你有pthread_create(..., my_callback, ...);
这样的函数,并且它希望你传递一个类型为void* (*) (void*)
的函数指针,那么你就不能传递一个不同格式的函数指针。这会调用未定义的行为,编译器可能会生成错误的代码。
话虽这么说,函数指针兼容性是许多系统上常见的非标准扩展。如果系统的调用约定是以函数格式不重要的方式指定的,并且特定的编译器端口支持它,那么这样的代码可能正常工作。
然而,此类代码不可移植且不标准。最好尽可能避免使用它。