简体   繁体   English

C回调-可选参数?

[英]C callbacks - optional argument?

Hey I have implemented some callbacks in my C program. 嘿,我在C程序中实现了一些回调。

typedef void (*server_end_callback_t)(void *callbackArg);

then I have variable inside structure to store this callback 然后我有可变的内部结构来存储此回调

server->server_end_callback = on_sever_end; 

What I have noticed it that I can pass in on_server_end callback function implementation that skips void *callbackArg and the code works correctly (no errors). 我注意到的是,我可以传入on_server_end回调函数实现,该实现将跳过void *callbackArg ,并且代码可以正常工作(无错误)。

Is it correct to skip some arguments like void * implementing callback functions which prototypes takes such arguments? 跳过一些参数,如void *实现回调函数(原型使用此类参数)是否正确?

void on_server_end(void) { 
 // some code goes here
}

I believe it is an undefined behavior from the C point of view, but it works because of the calling convention you are using. 从C的角度来看,我认为这是未定义的行为,但是由于您使用的调用约定,所以它可以工作。

For example, AMD64 ABI states that the first six arguments get passed to the calling function using CPU registers, not stack. 例如,AMD64 ABI指出,前六个参数是使用CPU寄存器而不是堆栈传递给调用函数的。 So neither caller nor callee need no clean-up for the first six arguments and it works fine. 因此,主叫方和被叫方都不需要对前六个参数进行清理,并且工作正常。

For more info please refer the Wikipedia. 有关更多信息,请参阅Wikipedia。

The code works correctly because of the convention of passing arguments. 由于传递参数的约定,因此代码可以正常工作。 Caller knows that callee expects some arguments - exactly one. 呼叫者知道被呼叫者期望一些参数-恰好是一个。 So, it prepares the argument(s) (either in register or on stack - depending on ABI on your platform). 因此,它准备参数(在寄存器中或在堆栈中-取决于平台上的ABI)。 Then callee uses those parameters or not. 然后,被调用方是否使用这些参数。 After return from callee, caller cleans up the stack if necessary. 从被呼叫者返回后,呼叫者将在必要时清理堆栈。 That's the mistery. 那是个谜。

However, you shall not abuse this specific behaviour by passing incompatible function. 但是,您不得通过传递不兼容的功能来滥用此特定行为。 It is a good practice to always compile your code with options -W -Wall -Werror (clang/gcc and compatible). 最好始终使用-W -Wall -Werror选项(clang / gcc和兼容)来编译代码。 Enabling such option would provide you a compilation error. 启用此选项将为您提供编译错误。

C allows a certain amount of playing fast and loose with function arguments. C允许使用函数参数快速和轻松地进行一定程度的播放。 So 所以

void (*fptr) ();

means "a pointer to a function which takes zero or more arguments". 表示“指向带有零个或多个参数的函数的指针”。 However this is for backwards compatibility, it's not wise to use it in new C code. 但这是为了向后兼容,在新的C代码中使用它并不明智。 The other way round 反过来

void (*fptr)(void *ptr)
{
   /* don't use the void */
}

/* in another scope */
 (*fptr)(); /* call with no arguments */

also works, as long as you don't use the void *, and I believe it is guaranteed to work though I'm not completely sure about that (on a modern machine the calling convention is to pass the first arguments in registers, so you just get a garbage register, and it will work). 只要您不使用void *,它也可以工作,并且我相信尽管我不确定(尽管在现代计算机上,调用约定是将寄存器中的第一个参数传递给我,所以可以保证它能工作)您只需获得一个垃圾寄存器,它就会起作用)。 Again, it is a very bad idea to rely on it. 同样,依靠它是一个非常糟糕的主意。

You can pass a void *, which you then cast to a structure of appropriate type containing as many arguments as you wish. 您可以传递一个void *,然后将其强制转换为适当类型的结构,该结构包含所需数量的参数。 That is a good idea and a sensible use of C's flexibility. 这是一个好主意,并且明智地使用了C的灵活性。

Is it correct to skip some arguments like void * implementing callback functions which prototypes takes such arguments? 跳过一些参数,如void *实现回调函数(原型使用此类参数)是否正确?

No it is not. 不它不是。 Any function with a given function declaration is not compatile with a function of a different function declaration. 具有给定函数声明的任何函数都不能与其他函数声明的函数兼容。 This rule applies for pointers to functions too. 此规则也适用于指向函数的指针。

So if you have a function such as pthread_create(..., my_callback, ...); 因此,如果您具有pthread_create(..., my_callback, ...);类的函数pthread_create(..., my_callback, ...); and it expects you to pass a function pointer of type void* (*) (void*) , then you cannot pass a function pointer of a different format. 并且它期望您传递void* (*) (void*)类型的函数指针,那么您将无法传递其他格式的函数指针。 This invokes undefined behavior and compilers may generate incorrect code. 这会调用未定义的行为,并且编译器可能会生成错误的代码。

That being said, function pointer compatibility is a common non-standard extension on many systems. 话虽这么说,功能指针兼容性是许多系统上常见的非标准扩展。 If the calling convention of the system is specified in a way that the function format doesn't matter, and the specific compiler port supports it, then such code might work just fine. 如果以函数格式无关紧要的方式指定了系统的调用约定,并且特定的编译器端口支持该约定,则此类代码可能就可以正常工作。

Such code is however not portable and not standard. 但是,这样的代码不是可移植的,也不是标准的。 It is best to avoid it whenever possible. 最好尽可能避免它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM