About function pointer declaration - c

#include <stdio.h>
void func(int ,int);
void (*fp)();
int main()
{
fp = func;
fp(10,20);
}
void func(int a,int b)
{
printf("%d %d\n",a,b);
}
In the above piece of code, the prototypes of function pointer and the function definition doesn't match. But still the code works. Please can any one help me to over come this doubt?

The declaration
void (*fp)();
says fp is a pointer to function that takes unspecified number of arguments, that returns void. So it's compatible with void func(int a,int b) {...}.
Declare it as:
void (*fp)(void);

void (*fp)(); // Pointer to function returning nothing, no argument info
void func(int ,int); // Function returning nothing with two intarguments
Paragraph 14 explains what the pointer is, the next paragraph explains why it fits.
6.7.6.3 Function declarators (including prototypes)
14 An identifier list declares only the identifiers of the parameters of the function. An empty
list in a function declarator that is part of a definition of that function specifies that the
function has no parameters. The empty list in a function declarator that is not part of a
definition of that function specifies that no information about the number or types of the
parameters is supplied.145)
15 For two function types to be compatible, both shall specify compatible return types.146)
Moreover, the parameter type lists, if both are present, shall agree in the number of
parameters and in use of the ellipsis terminator; corresponding parameters shall have
compatible types. If one type has a parameter type list and the other type is specified by a
function declarator that is not part of a function definition and that contains an empty
identifier list, the parameter list shall not have an ellipsis terminator and the type of each
parameter shall be compatible with the type that results from the application of the
default argument promotions. 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, and the type of each prototype parameter shall be
compatible with the type that results from the application of the default argument
promotions to the type of the corresponding identifier. (In the determination of type
compatibility and of a composite type, each parameter declared with function or array
type is taken as having the adjusted type and each parameter declared with qualified type
is taken as having the unqualified version of its declared type.)
Also of interest are the "future language directions", because they spell out that it's a backwards-compatibility feature.
6.11 Future language directions
6.11.6 Function declarators
1 The use of function declarators with empty parentheses (not prototype-format parameter
type declarators) is an obsolescent feature.
You can ask GCC to warn about function declarations without prototype using -Wstrict-prototypes, though you get false positives too:
http://coliru.stacked-crooked.com/a/e9eb7a7d2e384b00

Related

Using void (*)() pointers for other functions

Is it legal to access pointers to functions with varying argument lists in via a void (*f)() pointer? The program below compiles without warnings with gcc and appears to run correctly, but is it legal C?
#include <stdio.h>
#include <stdlib.h>
typedef void funp();
static void funcall( funp* F, int args, double x)
{
switch( args)
{
case 0: F(); break;
case 1: F(x); break;
}
}
static void fun0( void)
{
printf( "zero\n");
}
static void fun1( double x)
{
printf( "one\t%f\n", x);
}
int main( )
{
funcall( (funp*)fun0, 0, 17.0);
funcall( (funp*)fun1, 1, 17.0);
return EXIT_SUCCESS;
}
I compiled this with
gcc -Wpedantic -Wall -Wextra -std=gnu11 -O2 -o ./funp funp.c
It would be undefined behavior if the nargs parameter didn't match the number of arguments the function took, but is it legal if there is a match?
In this particular case, the calls are legal.
Section 6.7.6.3p15 of the C standard spells out what makes two function type compatible (relevant part in bold):
For two function types to be compatible, both shall specify
compatible return types. Moreover, the parameter type lists, if
both are present, shall agree in the number of parameters and
in use of the ellipsis terminator; corresponding parameters
shall have compatible types. If one type has a parameter type list
and the other type is specified by a function declarator that is
not part of a function definition and that contains an empty
identifier list, the parameter list shall not have an ellipsis
terminator and the type of each parameter shall be compatible with
the type that results from the application of the default
argument promotions. 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, and the type of each prototype parameter shall
be compatible with the type that results from the application
of the default argument promotions to the type of the
corresponding identifier. (In the determination of type
compatibility and of a composite type, each parameter declared
with function or array type is taken as having the adjusted type
and each parameter declared with qualified type is taken as having the
unqualified version of its declared type.)
So you have a typedef with type:
void()
And functions with type:
void(void)
void(double)
The two function definitions don't use ellipsis (...) so that satisfies the fist condition. For the second condition, let's look at what the default argument promotions are. Those are specified in section 6.5.2.2p6:
If the expression that denotes the called function has a
type that does not include a prototype, the integer promotions
are performed on each argument, and arguments that have type
float are promoted to double. These are called the default
argument promotions.
The first function has no arguments, so it is compatible. The second function has a single double argument, which matches the default argument promotions, so it is also compatible.
To give some more examples, the following functions would also be compatible:
void f1(long);
void f2(int);
But these would not:
void f3(float);
void f4(char);
void f5(short);
As another answer notes, the code you showed is valid C today. But that may change at any point in the future, due to the use of a function type with no parameter list.
6.11 Future language directions
6.11.6 Function declarators
1 The use of function declarators with empty parentheses (not
prototype-format parameter type declarators) is an obsolescent
feature.
An obsolescent feature is one that is subject to removal in future standard versions. So if you wish your code to be future proof, it's best to avoid it.
As mentioned in #StoryTeller's answer, the use of function declarators with empty parentheses is an obsolescent feature, but it can be avoided:
#include <stdio.h>
#include <stdlib.h>
typedef void funp(void);
static void funcall( funp* F, int args, double x)
{
switch( args)
{
case 0:
F();
break;
case 1:
{
typedef void fn(double);
((fn *)F)(x);
}
break;
}
}
static void fun0( void)
{
printf( "zero\n");
}
static void fun1( double x)
{
printf( "one\t%f\n", x);
}
int main( void )
{
funcall( (funp*)fun0, 0, 17.0);
funcall( (funp*)fun1, 1, 17.0);
return EXIT_SUCCESS;
}
EDIT: Changed parameter list of main to void for compliance.
In answer to the query:
"Moreover, the parameter type lists, if both are present, shall agree in the number of parameters" would seem to mean that the types of funp and of fun1 are incompatible. Is it ok to cast?
The answer is yes, it is OK to cast. From C11 draft 6.3.2.3 para 8:
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.
In the code, the pointer to fun1 has been converted to a different function pointer type in the call to funcall, and converted back to the original type within funcall so can be used to call fun1.

I want default argument promotion's example

At C standard 6.5.2.2. paragraph 6
If the expression that denotes the called function has a type that does not include a prototype, the
integer promotions are performed on each argument, and arguments that have type float are
promoted to double. These are called the default argument promotions.
"the expression that denotes the called function has a type that does not include a prototype" What can this be described as code?
char a = 1;
printf("%d", a); /*"the expression that denotes the called function" is printf, and `a`
doesn't have prototype(char). but a can't be replaced as `char a` originally. ...? */
This is my thought that can be applicable to above standard. Is this right?
For historical reasons, functions in C can be declared the old way, without a prototype, as:
type function()
or the new way, with a prototype, as:
type function(type parameter)
type function(type parameter, type parameter)
type function(type parameter, type parameter, type parameter)
… and so on
Thus, a function declaration in which the types of the parameters are given is said to have a prototype. This affects preparation of the arguments when calling a function. With a prototype, the arguments are converted to the declared types of the parameters. Without a prototype, the arguments are converted to default types, using rules called the default argument promotions. (Also, if a function is just being declared, not defined, the names of the parameters can be omitted, leaving a prototype that just lists types, as in type function(type, type, type).)
Because of the old grammar, to declare a function with a prototype that says it has no parameters, you need to explicitly say void: type function(void). (This is different in C++, which does not have to support the old grammar. In C++, type function() is a prototype with no parameters.)
Additionally, a prototype can specify that there are variable arguments by putting ,... after one or more regular parameters:
type function(type parameter,...)
In this case, the first arguments are converted to the parameter types, but the arguments that correspond to the ... are converted using the default argument promotions. printf is declared this way.
The default argument promotions are:
Integers narrower (technically, of lesser rank) than int are promoted to int if it can represent all the values of the source type or unsigned int otherwise.
float arguments are converted to double.
There is also some finickiness about bit-fields in the default argument promotions, which I cannot say has ever arisen in code for me.
History
In the old C grammar, a function would be defined with:
type function(name0, name1, name2)
type name0;
type name1;
type name2;
and it would be declared without a prototype, as with type function(). This means the caller did not know the actual types of the parameters. But you could not just pass a char value for a char argument or a short value for a short argument, because C, in trying to be flexible so it could work on many types of computers, had rules about char and short values being promoted to int in expressions. Additionally, character constants like 'X' have type int, not char, so, if somebody called a function with foo('X'), the compiler would not know if foo really wanted just a char rather than an int. So the default argument promotions were made to match the integer promotions.
Later versions of C fixed this by providing a way to declare the argument types in declarations visible to the caller, so the arguments always match the parameters (and the compiler has more information it can use to provide error messages). But the old grammar still has to be supported so that old code can be compiled.
More
The phrase in the C standard, “the expression that denotes the called function,” is used because you can call functions through pointers, not just their names. For example, we can write:
int (*FunctionPointer)() = (int (*)()) printf;
and then call printf using FunctionPointer("Hello, world.\n");. Then “the expression that denotes the called function” is FunctionPointer, and it does not have a prototype even though printf does. (There is no good reason to do this with the printf function, but some esoteric code may do some unsavory things.)
You can initially declare a function without a prototype:
int foo();
and later add a prototype:
int foo(float x, char *y);
The compiler will merge the declarations, and the resulting foo will have a prototype.
The expression that denotes the called function is printf, right.
And if you don't provide a prototype for this function, for example by not including <stdio.h> and not declaring of printf() yourself, the compiler recognizes the function as prototype-less.
In your case the first argument is a const char *, and the second argument is an int.
So, it is not a that has no prototype, but printf().
The value of a will be promoted to int.
The assumed prototype will be int printf(); and takes any number and any type of parameters.

Compatibility of function types that does not include a prototype

There is a rule for function type compatibility N2310 6.7.6.3(p15):
If one type has a parameter type list and the other type is specified
by a function declarator that is not part of a function definition and
that contains an empty identifier list, the parameter list shall not
have an ellipsis terminator and the type of each parameter shall be
compatible with the type that results from the application of the
default argument promotions.
I can imagine an example:
#include <stdio.h>
int foo();
float bar();
int main(void){
printf("%d\n", foo(1, 3)); //fine, int is unchanged by default argument promotion
printf("%f\n", bar(1.0f, 2.0f)); //error, float is promoted to double
}
int foo(int a, int b){
return a + b;
}
float bar(float b, float c){
return b + c;
}
The thing that I found contradictory was that 6.5.2.2(p6) mentions that :
If the number of arguments does not equal the number of parameters,
the behavior is undefined.
In the case of int foo() it has an empty identifier-list. So does a call printf("%d\n", foo(1, 3)); yield UB (2 arguments were supplied)?
Anyway the rules look pretty strange and kind of unnatural. What was the reason for that? I suppose some backward compatibility with previous versions of the Standard... ?
C 2018 6.7.6.3 15 tells you whether two types are compatible. So it can be used to compare two declarations. For foo, you have:
int foo();
int foo(int a, int b) {...}
Of these, the second has a parameter list, and the first is specified by a function declaration that is not part of a function definition and that contains an empty identifier list. So the rule is 6.7.6.3 15 applies. It says the parameter list shall not have an ellipsis terminator (and it does not), and that the type of each parameter shall be compatible with the type that results from the default argument promotions (and they are, since int produces int).
Then, for bar, we have:
float bar();
float bar(float b, float c) {...}
Again, 6.7.6.3 15 applies. But in this case, each parameter has a type that does not result from the default argument promotions, since the default promotions convert float to double. So these two declarations declare bar with incompatible types.
Regarding 6.5.2.2 6:
… If the number of arguments does not equal the number of parameters, the behavior is undefined…
This refers to the number of parameters of the actual function, not the number of parameters appearing in the (empty) list in the declaration.
Anyway the rules look pretty strange and kind of unnatural. What was the reason for that? I suppose some backward compatibility with previous versions of the Standard... ?
Yes, C was originally lax about function declarations, allowing functions to be declared with empty parameter lists, if I recall correctly, all arguments were passed with the promoted types. Support for stricter and more precise declarations came later, and the rules are written to allow old code to continue to work.
Note that the rule about compatibility of function types is relevant for the declarations of the functions, but not the calls.
When a function call is being analyzed by the compiler, the rules in 6.5.2.2 are used to prepare the call. These rules say the arguments are treated in various ways according to the declaration of the function that is visible at the point of the call. (Technically, to the type of the expression that denotes the called function. This is often a function name but could be a pointer to a function, including one computed by a cast expression.)
The rules about compatibility assure you that, if you call a function using a type that is compatible with the type of the actual function definition, then the call has defined behavior.

The void type in C

The void type in C seems to be strange from various different situations. Sometimes it behaves like a normal object type, such as int or char, and sometimes it just means nothing (as it should).
Look at my snippet. First of all, it seems strange that you can declare a void object, meaning you just declare nothing.
Then I created an int variable and casted its result to void, discarding it:
If an expression of any other type is evaluated as a void
expression, its value or designator is discarded. (ISO/IEC 9899:201x, 6.3.2.2 void)
I tried to call my function with a void cast, but my compiler gave me (Clang 10.0):
error: too many arguments to function call, expected 0, have 1
So the void in a prototype means nothing, and not the type void.
But then, I created a pointer to void, dereferenced it, and assigning the “result” to my int variable. I got the “incompatible type” error. That means the void type does exist here.
extern void a; // Why is this authorised ???
void foo(void); // This function takes no argument. Not the 'void' type.
int main(void)
{
int a = 42;
void *p;
// Expression result casted to 'void' which discards it (per the C standard).
(void)a;
// Casting to 'void' should make the argument inexistant too...
foo((void)a);
// Assigning to 'int' from incompatible type 'void': so the 'void' type does exists...
a = *p;
// Am I not passing the 'void' type ?
foo(*p);
return 0;
}
Is void an actual type, or a keyword to means nothing ? Because sometimes it behaves like the instruction “nothing is allowed here”, and sometimes like an actual type.
EDIT: This questions is NOT a duplicate. It is a purely about the semantics of the void type. I do not want any explanation about how to use void, pointers to void or any other things. I want an answer per the C standard.
In C language the void type has been introduced with the meaning of 'don't care' more than 'null' or 'nothing', and it's used for different scopes.
The void keyword can reference a void type, a reference to void, a void expression, a void operand or a void function. It also explicitly defines a function having no parameters.
Let's have a look at some of them.
The void type
First of all void object exists and have some special properties, as stated in ISO/IEC 9899:2017, §6.2.5 Types:
The void type comprises an empty set of values; it is an incomplete object type that cannot be completed.
Pointers
The more useful reference to void, or void *, is a reference to an incomplete type, but itself is well defined, and then is a complete type, have a size, and can be used as any other standard variable as stated in ISO/IEC 9899:2017, §6.2.5 Types:
A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.
Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements.
All pointers to structure types shall have the same representation and alignment requirements as each other.
All pointers to union types shall have the same representation and alignment requirements as each other.
Pointers to other types need not have the same representation or alignment requirements.
Casting to void
It can be used as cast to nullify an expression, but allowing the completion of any side effect of such expression. This concept is explained in the standard at ISO/IEC 9899:2017, §6.3 Conversions, §6.3.2.2 void:
The (nonexistent) value of a void expression (an expression that has type void) shall not be used in any way, and implicit or explicit conversions (except to void) shall not be applied to such an expression.
If an expression of any other type is evaluated as a void expression, its value or designator is discarded. (A void expression is evaluated for its side effects.)
A practical example for the casting to void is its use to prevent warning for unused parameters in function definition:
int fn(int a, int b)
{
(void)b; //This will flag the parameter b as used
... //Your code is here
return 0;
}
The snippet above shows the standard practice used to mute compiler warnings. The cast to void of parameter b acts as an effective expression that don't generate code and marks b as used preventing compiler complains.
void Functions
The paragraph §6.3.2.2 void of the standard, covers also some explanation about void functions, that are such functions that don't return any value usable in an expression, but functions are called anyway to implement side effects.
void pointers properties
As we said before, pointers to void are much more useful because they allow to handle objects references in a generic way due to their property explained in ISO/IEC 9899:2017, §6.3.2.3 Pointers:
A pointer to void may be converted to or from a pointer to any object type.
A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
As practical example imagine a function returning a pointer to different objects depending on input parameters:
enum
{
FAMILY, //Software family as integer
VERSION, //Software version as float
NAME //Software release name as char string
} eRelease;
void *GetSoftwareInfo(eRelease par)
{
static const int iFamily = 1;
static const float fVersion = 2.0;
static const *char szName = "Rel2 Toaster";
switch(par)
{
case FAMILY:
return &iFamily;
case VERSION:
return &fVersion;
case NAME:
return szName;
}
return NULL;
}
In this snippet you can return a generic pointer that can be dependent on input par value.
void as functions parameter
The use of void parameter in functions definitions was introduced after the, so called, ANSI-Standard, to effectively disambiguate functions having variable number of arguments from functions having no arguments.
From standard ISO/IEC 9899:2017, 6.7.6.3 Function declarators (including prototypes):
The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.
Actual compilers still support function declaration with empty parenthesis for backward compatibility, but this is an obsolete feature that will eventually be removed in future release of standard. See Future directions - §6.11.6 Function declarators:
The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent
feature.
Consider the following example:
int foo(); //prototype of variable arguments function (backward compatibility)
int bar(void); //prototype of no arguments function
int a = foo(2); //Allowed
int b = foo(); //Allowed
int c = bar(); //Allowed
int d = bar(1); //Error!
Now resembling your test, if we call the function bar as follows:
int a = 1;
bar((void)a);
Triggers an error, because casting to void an object doesn't null it. So you are still trying to pass a void object as parameter to a function that don't have any.
Side effects
As requested this is a short explain for side effects concept.
A side effect is whichever alteration of objects and values derived from the execution of a statement, and which are not the direct expected effect.
int a = 0;
(void)b = ++a;
In the snippet above the void expression lose the direct effect, assigning b, but as side effect increase the value of a.
The only reference, explaining the meaning, in the standard can be found in 5.1.2.3 Program execution:
Accessing a volatile object, modifying an object, modifying a
file, or calling a function that does any of those operations are all
side effects, which are changes in the state of the execution
environment.
Evaluation of an expression in general includes both value
computations and initiation of side effects.
void is a type. Per C 2018 6.2.5 19, the type has no values (the set of values it can represent is empty), it is incomplete (its size is unknown), and it cannot be completed (its size cannot be known).
Regarding extern void a;, this does not define an object. It declares an identifier. If a were used in an expression (except as part of a sizeof or _Alignof operator), there would have to be a definition for it somewhere in the program. Since there cannot a definition of void object in strictly conforming C, a cannot be used in an expression. So I think this declaration is allowed in strictly conforming C but is not useful. It might be used in C implementations as an extension that allows getting the address of an object whose type is not known. (For example, define an actual object a in one module, then declare it as extern void a; in another module and use &a there to get its address.)
The declaration of functions with (void) as a parameter list is a kludge. Ideally, () might be used to indicate a function takes no parameters, as is the case in C++. However, due to the history of C, () was used to mean an unspecified parameter list, so something else had to be invented to mean no parameters. So (void) was adopted for that. Thus, (void) is an exception to the rules that would say (int) is for a function taking an int, (double) is for a function taking a double, and so on—(void) is a special case meaning that a function takes no parameters, not that it takes a void.
In foo((void) a), the cast does not make the value “not exist.” It converts a to the type void. The result is an expression of type void. That expression “exists,” but it has no value and cannot be used in an expression, so using it in foo((void) a) results in an error message.
From C Standard#6.2.5p19:
19 The void type comprises an empty set of values; it is an incomplete object type that cannot be completed.
This indicate that the void type exists.
Doubt 1:
void foo(void); // This function takes no argument. Not the 'void' type.
Correct.
From C Standard#6.7.6.3p10 [emphasis mine]:
10 The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.
This is a special case they had to add to the language syntax because void foo(); already meant something different (void foo(); doesn't specify anything about foo's parameters). If it weren't for the old meaning of void foo();, void foo(); would have been the syntax to declare a no-argument function. You can't generalize anything from this. It's just a special case.
Doubt 2:
// Casting to 'void' should make the argument inexistant too...
foo((void)a);
No, it will not because void is also an object type though it is incomplete.
Doubt 3:
// Assigning to 'int' from incompatible type 'void': so the 'void' type does exists...
a = *p;
Yes, it does exist and hence the compiler is reporting error on this statement.
Doubt 4:
// Am I not passing the 'void' type ?
foo(*p);
Declaration of foo() function:
void foo(void);
^^^^
The void in parameter list indicates that function will not take any argument because it has been declared with no parameters.
Just for reference, check this from C Standard#5.1.2.2.1p1 [emphasis mine]:
1 The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
^^^^
Doubt 5:
extern void a; // Why is this authorised ???
This is authorized because void is a valid type and it is just a declaration. No storage will allocate to a.
In C, void can't be considered as a data type, it is a keyword used as a placeholder in place of a data type to show that actually there is no data. Hence this
void a;
is not valid.
while here
void foo(void);
void keyword is used to inform to the compiler that foo is not going to take any input argument nor it has return type.
In below case
int a = 42;
void *p;
a = *p; /* this causes error */
a = *p; is wrong because you can't dereference void pointer directly, you need to perform proper type casting first. for e.g
a = *(int*)p; /* first typecast and then do dereference */
Also this
foo(*p);
is wrong because of two reason,
firstly foo() doesn't expects any argument.
secondly you can't do *p as p is void pointer. Correct one is foo(*(int*)p); if foo() declaration is void foo(int);.
Note that this
(void)a;
doesn't do anything so your compiler might not giving any warning but when you do like
int b = (void)a;
compiler won't allow as void is not consider as data type.
Finally this
extern void a; // Why is this authorised ???
this is just a declaration not definition, a doesn't exist until you define it, since a is having extern storage class, you need to define somewhere & when you are going define like
a = 10;
compiler throws a error as
error: ‘a’ has an incomplete type
From C standard 6.2.5 Types
The void type comprises an empty set of values; it is an
incomplete object type that cannot be completed.
6.3.2.2 void
The (nonexistent) value of a void expression (an expression that has
type void) shall not be used in any way, and implicit or explicit
conversions (except to void) shall not be applied to such an
expression. If an expression of any other type is evaluated as a
void expression, its value or designator is discarded. (A void
expression is evaluated for its side effects.)
6.3.2.3 Pointers
A pointer to void may be converted to or from a pointer to any
object type. A pointer to any object type may be converted to a
pointer to void and back again; the result shall compare equal to the
original pointer.
A storage-class specifier or type qualifier modifies the keyword
void as a function parameter type list (6.7.6.3).
An attempt is made to use the value of a void expression, or an
implicit or explicit conversion (except to void) is applied to a
void expression (6.3.2.2).
First of all, it seems strange that you can declare a void object, meaning you just declare nothing.
void is an incomplete object type that cannot be completed. This mostly defines its uses in regular contexts, i.e. contexts that do not provide special treatment for void. Your extern declaration is one of such regular contexts. It is OK to use an incomplete data type in a non-defining declaration.
However, you will never be able to provide a matching definition for that declaration.
So the void in a prototype means nothing, and not the type void.
Correct. The parameter must be unnnamed. And the (void) combination is given special treatment: it is not one parameter of type void, but rather no parameters at all.
But then, I created a pointer to void, dereferenced it, and assigning the “result” to my int variable. I got the “incompatible type” error. That means the void type does exist here.
No. It is illegal to apply unary * operator to a void * pointer. Your code is invalid for that reason already. Your compiler issued a misleading diagnostic message. Formally, diagnostic messages are not required to properly describe the root of the problem. The compiler could've just said "Hi!".
Is void an actual type, or a keyword to means nothing ?
It is a type. It is an incomplete object type that cannot be completed.

Recursively declare function in C language

got a question from adream307, I have no idea, what about yours?
I want to declare a function like this:
(we named this type of function as F)
the return type of F is "void"
the parameter of F is a function pointer, this pointer point to a
function whose type is the same as F
can i declare a function like this?
No you cannot. The type cannot be expressed, since it would repeat itself:
void f(void g(void h(...
But you can write a function which accepts itself, without any problems. Consider
void f(void g()) { }
int main(void) { f(f); }
That's perfectly fine. The parameter type of f is a function pointer (void g() is equivalent to void (*g)() here) whose type is compatible with the type of f. The rule for compatibility for the function types of both the parameter of f and the argument in the call, void() and void (void()) is specified as:
If one type has a parameter type list [the call argument] and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifer list [the function parameter type], the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions.
Both types satisfy this compatibility rule, so the function call is well defined.

Resources