Reputation: 812
The following two are the same but C99 standard leaves casting from void *
to a function pointer undefined.
Can somebody explains how the second one works? Its a little bit confusing!
int (*fptr)(int);
fptr = (int (*)(int))dlsym(handle, "my_function");
int (*fptr)(int);
*(void **) (&fptr) = dlsym(handle, "my_function");
Upvotes: 4
Views: 1697
Reputation: 1141
The reason the second version is legal is in the following part of the ISO Standard:
6.3.2.3 Pointers 1 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.
(**void)
, as well as &fptr
are pointers to objects (since &fptr is a pointer to a pointer to a function), so the cast in your second expression is explicitly allowed by the statement above. Your first expression is trying to convert a pointer to an object (rather a pointer to void
which is a special case in the standard) to a pointer to a function (which is not an object), which is not allowed by the standard as you pointed out.
Upvotes: 1
Reputation: 1401
On the first line (of the second code paste) declares a function pointer (I imagine that you know that).
Now, dlsym(3)
is a call that returns a void *
.
So the second line can also be read as:
*((void **) (&fptr)) = dlsym(handle, "function");
Otherwise said: instead of casting the function result as int (*)(int)
, and affecting the given result to fptr; it casts a pointer on fptr (or it casts fptr's address: a pointer on a pointer) as a void**
. Then it dereferences this pointer, effectively giving fptr (the same as the original one, but without the int (*)(int)
type), which then gets the result of the dlsym
call. It's just a way to 'trick' the compiler into not triggering warnings/errors about a type mismatch. Please also note that even if the syntax you chose is a matter of taste, it's something you should fully understand before you use it in any program you release.
I hope it helps ;)
Upvotes: 3
Reputation: 6608
The difference is the same as the one between:
float f = 3.14;
int i = (int)f;
and
float f = 3.14;
int i = *(int*)&f;
The first is a regular cast of the value, which in some cases (int <--> float, or back in the 8086 days near pointer <--> far pointer) causes some conversions; and in some cases (some casts between function pointers and regular pointers) doesn't even compile.
The second is a raw bitwise copy, which the compiler will always accept but bypasses any conversion and may lead to writing a variable into another variable of different size. Can be highly dangerous, especially with function pointers.
Upvotes: 2