Reputation: 39
#include <stdio.h>
struct A {
int data; // this is data
void *(*function)(); // this is operation
};
void myfun1() {
printf("this is fun()\n");
}
int myfun2(int a) {
printf("this is fun(%d)\n", a);
return a;
}
char myfun3(int a) {
printf("this is fun(%c)\n", a);
return a;
}
int main(void) {
struct A a;
a.function = (void *)myfun2;
a.function('a');
a.function = (void *)myfun3;
a.function('a');
return 0;
}
I wonder why
a.function = (void *)myfun2;
Because I first thought it can be
a.function =(void *(*)())myfun2;
Why does the first one work out and mine is wrong?
Upvotes: 1
Views: 149
Reputation: 213276
Conversions between a function pointer and any other pointer type is outside the scope of the C standard. C only specifies what will happen when you cast between void* and a pointer to object type (6.3.2.3).
Conversions between two different function pointers are allowed, as long as the types are compatible. If they aren't compatible, you are invoking undefined behavior (6.3.2.3/8).
I believe C and C++ work exactly the same. The fact that C allows implicit conversions between void* and pointers to object is completely irrelevant to the question.
So to answer your questions:
why the pointer to function can be cast in this way?
It cannot, unless you rely on non-standard extensions.
Why does the first one work out and mine is wrong?
The first one does not work unless you rely on non-standard extensions.
The second one may or may not work, depending on function calling conventions on the specific system.
Upvotes: 1
Reputation: 25908
What you're doing is partially OK, but the bit that isn't OK is probably the most important bit.
In C, void *
is a generic pointer for any object type (C11 6.3.2.3.1):
A pointer to
void
may be converted to or from a pointer to any object type.
It is undefined behavior to convert a pointer to a function to a void *
. A function is not an object.
It is OK to convert a function to a function of one type to a pointer to a function of another type, and back again, without loss of information (C11 6.3.2.3.8). In your case, you are converting from, in one case, an int (*)(int)
to a void *(*)()
. The (void *(*)())
cast would be OK, and the (void *)
case would not be OK.
Where you run into trouble is when you try to actually call that function from the converted type. Again, from C11 6.3.2.3.8:
If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.
So in your case, when you convert myfun2()
, for instance, to a.function
with a.function =(void *(*)())myfun2
, the conversion itself is fine. But what you cannot do is then call that function via a.function('a')
, because the types of the two functions are not compatible, so you're into undefined behavior.
Since you define A.function
as a pointer to a function with an unspecified number of arguments, you may be OK for compatibility on the parameter front per C11 6.7.6.3.15:
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
and since there is only one parameter of type int
in myfun2()
, this is satisfied. What will really mess you up is the return type, since void *
and int
are incompatible, so you have undefined behavior. In reality, on most modern 64 bit systems, void *
and int
will not be the same size, so you could expect unpredictable results trying to do this on such a system even if it will let you.
Because the conversion itself is OK, you can use any function pointer to store any other, so if you wanted to you could store additional information about the function type in the struct, and use it later to convert to a compatible type, for instance:
if ( a.return_type == RET_TYPE_INT && a.arg_type == ARG_TYPE_INT ) {
int (*fp)(int) = (int(*)(int)) a.function;
int n = fp('a');
/* Do something with n */
}
else if ( a.return_type = RET_TYPE_CHAR && a.arg_type == ARG_TYPE_INT ) {
char (*fp)(int) = (char(*)(int)) a.function;
char ch = fp('a');
/* Do something with ch */
}
and so on, where RET_TYPE_CHAR
and ARG_TYPE_INT
are constants you've defined yourself, and return_type
and arg_type
are additional members of struct A
that you'd populate when setting a.function
.
Upvotes: 2
Reputation: 30489
a.function =(void *(*)())myfun2;
I guess you are think ()
after function name implies function doesn't take any argument. It actually means function can take any number of argument.
If you want such senseless code to be illegal, you should change your structure as following
struct A {
int data; /* this is data */
void *(*function)(void); /* this is operation */
};
If I am mis-interpreting your question, Sergey L. has already answered the other interpretation.
Upvotes: 1
Reputation: 10417
Um, it's worng if you're in C++.
But in C, void *
can be cast implicit.
- cast to void* explicit
- cast to void*(*) implicit
Upvotes: -2