Aux
Aux

Reputation: 134

What's the use of __cdecl in function arguments in C

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

Answers (4)

SKi
SKi

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

popzxc
popzxc

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

Lundin
Lundin

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

Candy Gumdrop
Candy Gumdrop

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

Related Questions