Reputation: 134
I am learning C language and while learning I found a line of code which is totally new and strange for me
void PullDown(char **, int, void (__cdecl **)(void));
I know about 1st and 2nd parameter only .
I want to know about 3rd parameter.
what's is the use of two asterisk after __cdecl ?
I am aware from this syntax (type_cast *)
so it is related to type casting ?
Upvotes: 1
Views: 7612
Reputation: 8476
The 3rd parameter is pointer to function pointer. With only one asterix, it would be function pointer.
__cdecl
is a compiler specific attribute that indicates that C calling convention must be used. See this page. If you play only with C or with other compiler, then you may ignore it.
Maybe example is helpful:
#include <stdio.h>
void PullDown(char **, int, void (**)(void));
int main(int argc, char **argv)
{
void (*fun)(void);
PullDown(NULL, 0, &fun);
fun();
return 0;
}
void my_function(void)
{
printf("Hello!\n");
}
void PullDown(char **param1, int param2, void (**param3)(void))
{
*param3 = my_function;
}
It prints "Hello!"
In the example, fun
is a function pointer variable. Pointer of fun
is passed to the PullDown()
function call. So PullDown()
can set pointer of my_function()
to the fun
.
Upvotes: 0
Reputation: 56
__cdecl
is the label of default C/C++ calling convention (named, surprisingly, cdecl
).
In simple words, calling convention is the set of rules describing how to call function in assembly (e.g. put arguments to the registers/stack, get the result from EAX/RAX register or from some other place).
You can read about those conventions more in the corresponding wiki page.
What you've got is the function PullDown
which takes 3 arguments, and the third one is a pointer to a pointer to a function that should satisfy cdecl
conventions.
Upvotes: 0
Reputation: 214850
void (*)(void)
is a function pointer to a function of the type void func (void)
.
void (**)(void)
is a pointer to a function pointer. Likely meaning that the caller expects this parameter to be written to, so that the function pointer is passed back to the caller.
void (__cdecl **)(void)
is the same, but with the non-standard extension __cdecl
. This specifies the calling convention of the function, ie who is responsible for stacking parameters. If it is the caller, then __cdecl
is used, if it is the function, then __stdcall
is used. Both of these two non-standard extensions are commonly used in Windows programming.
In this case it specifies the calling convention for the function pointed at.
Upvotes: 0
Reputation: 2795
__cdecl
is a C language extension supported by Microsoft's compiler. It specifies explicitly that a function should be called with "cdecl" calling conventions, which relates to the internals of exactly how the state of registers and the stack should be set up before and after calling a function, in order to pass arguments and return values.
In your code snippet, PullDown
is defined to be a function with three arguments, the first two of which being a char **
and an int
.
The last argument to the function, void (__cdecl **)(void)
, is a pointer to a pointer to a function with cdecl calling conventions which has no return value and takes no arguments.
To break this declaration down, we can remove __cdecl
completely for now and add a variable name for this parameter:
void (**param)(void)
The *
operator in declarations specifies that the expression to the right of it is a pointer, so this means param
is a pointer, and also *param
is a pointer (therefore param
is a pointer to a pointer). To understand what this pointer is pointing to, **param
can be substituted with the placeholder foobar
for now to give the following:
void (foobar)(void)
This now has a redundant pair of parentheses and is equivalent to the following:
void foobar(void)
This now looks like a regular function declaration returning void
with a void
argument (no arguments and no return value). Therefore param
is a pointer to a pointer to a function with this signature.
Lastly, __cdecl
applies to the expression to the right of it, and since **param
represents the function, __cdecl
can be added to the left of **param
to indicate that this function has cdecl calling conventions:
void (__cdecl **param)(void)
The parameter in your code snippet simply has the parameter name param
removed, in the same way it is removed from char **param
and int param
.
Generally speaking, cdecl calling conventions should be the default when compiling C and C++ code with Visual Studio, so specifying __cdecl
explicitly should be redundant. However, there are times when it is necessary to specify that a function has __stdcall
calling conventions for example, and it is important when dealing with function pointers to ensure stdcall functions are only called via __stdcall
function pointers and cdecl functions are only called via __cdecl
function pointers (which should be the default). Attempting to call a function with the wrong calling convention will most likely crash your program or leave it in an indeterminate state.
Upvotes: 4