4

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"); 

3 Answers 3

3

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 ;)

Sign up to request clarification or add additional context in comments.

7 Comments

@OliCharlesworth: No.
The only difference is the extra parentheses, and they have no effect.
@OliCharlesworth: Right. That's why they weren't there in the original paste. Now I added them to explain what's happening, since the question is: "Can somebody explains how the second one works?". Are you here to contribute or to troll?
I'm here to vote on answers, same as everyone else. Your answer appears to be claiming that the OP's second snippet should have these extra parentheses. It should not (perhaps it just needs rewording to make it clear what you mean...)
@OliCharlesworth: better? :)
|
2

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.

1 Comment

+1. However, note that POSIX-compatible systems must support the OP's second version in the case of dlsym() (see the "Rationale" section here: pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html).
1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.