26

While following some tutorials and reading about function pointers I learned that evidently assigning a void pointer to a function pointer in ISO C is undefined, is there any way to resolve the warning I receive during compile time (e.g. a better way of coding it) or should I just ignore it?

Warning:

ISO C forbids assignment between function pointer and 'void *' [-pedantic] 

Example Code:

void *(*funcPtr)(); funcPtr = GetPointer(); 

GetPointer is a function that returns a void pointer E.G.

void *GetPointer(); 
10
  • 3
    How about not assigning a void pointer to a function pointer? Commented Jan 3, 2013 at 6:47
  • 4
    It is undefined in theory, because some machines used to have different size for code addresses & data addresses. In practice, on most common architecture today, code & data addresses are of the same size, in the same address space. Commented Jan 3, 2013 at 6:48
  • 11
    @RobertHarvey: a common example is dlsym in Posix. You have to assign its result to function pointers (an early standard draft proposed some other name, maybe dlfsym, to return a function pointer, but that never happened) Commented Jan 3, 2013 at 6:50
  • 1
    @JonathanWood GetPointer returns a void* I failed to note that in my original post. Commented Jan 3, 2013 at 6:54
  • 1
    @JonathanWood Casting doesn't make it any better here, since the conversion is prohibited and not only implicit conversion. So this includes explicit conversion (casting) as well. Commented Jan 3, 2013 at 7:01

3 Answers 3

15

In tlpi-book I found this trick very interesting:

#include <dlfcn.h> int main(int argc, char *argv[]) { ... void (*funcp)(void); /* Pointer to function with no arguments */ ... *(void **) (&funcp) = dlsym(libHandle, argv[2]); } 
Sign up to request clarification or add additional context in comments.

4 Comments

@MatthieuPoullet: On the right hand side, we have a void pointer(void *). On the left hand side, we take the address of a function pointer. Ignoring the (void **) for a moment, the pointer to a function pointer is dereferenced by the left-most *. The (void **) is just saying that the pointer to a function pointer should be cast to a pointer to a void pointer, which, when dereferenced by the left-most * is thus a void *. Thus, both left and right hand sides of the assignment have type void * and the compiler is happy.
This is a strict aliasing violation and has thus undefined behavior. And even if that was fixed by using memcpy, we'd still rely on something that we shouldn't rely on, namely the binary representation of a function pointer being identical to the representation of a data pointer. Better just do a normal cast and locally disable -Wpedantic. Working around a warning by doing something worse that the compiler just happens to have no warning for is not something I would recommend.
So confusing and unclear. What are the purposes/functions of all of: dlfcn.h, dlsym, libHandle, void **?
@WhyWhat: dlsym() is a POSIX / Unix function that returns a function pointer as a void*. (It's defined in dlfcn.h). This answer is just using it as an example of a reason you might need to cast a void* to a void (*)(void) function pointer, perhaps the use-case where they encountered a need for that. Agreed that it's a poor way to write an answer, vs. just void *vp as a function arg or something. The *(void **) &var = ... type-pun is the actual answer, which violates the strict-aliasing rule; GCC doesn't define the behaviour
8

No. The compiler is right, and you too: in C89 and C99, you can't convert between data pointers (which void * is) and function pointers, so the only way for resolving the warning is returning a function pointer from the function.

(Note, however, that in practice this works despite the warning, and even there's this inconsistency in the standard library - the dlsym() function is used for obtaining function pointers, but it returns void * - so essentially you can ignore the warning. It will work, although strictly speaking the behavior is undefined here.)

7 Comments

Indeed the code works I was just interested in learning about better possible coding styles (Thus why I code with such strict warnings), it's less about not having warnings and more about understanding their causes, effects, and ways to possibly avoid them.
@Tomwaivory Yes, I see, and that's definitely a good practice. In this case, it's something that is wrong with the language :) So don't worry, practice is not the same as theory, unless you're working on a DS9k, it will work with this UB inside, and you can go beat the members of the C standards comittee with a stick :D
Thanks that's good to know, I can't fix the language just do my best to code around it :)
"in practice this works" On most architectures. There are some (mostly arcane) architectures where it does not work. Also dlsym is not a "standard library" function, it is a Unix library function. And it's not "something that is wrong with the language", because there's a reason for it (some places it does not work).
@newacct Don't split hair, please. You're right. Requirements C imposes were designed with portability in mind - however some of them are quite uncommon or localized nowadays. "dlsym() is not a standard library function" - it is, it's just not part of the ISO/ANSI C standard library, but the POSIX C standard library.
|
5

I encountered this problem using glib. Glib data structures, such as GSList usually have a field called void *data. I wanted to store functions in a list and got a bunch of errors similar to this:

warning: ISO C forbids passing argument 2 of ‘g_slist_append’ between function pointer and ‘void *’ [-pedantic] 

This example generates a bunch of warnings using gcc -Wall -ansi -pedantic

typedef int (* func) (int); int mult2(int x) { return x + x; } int main(int argc, char *argv[]) { GSList *functions = NULL; func f; functions = g_slist_append(functions, mult2); f = (func *) functions->data; printf("%d\n", f(10)); return 0; } 

So I wrapped the function in a struct and all the warnings go away:

struct funcstruct { int (* func) (int); }; int mult2(int x) { return x + x; } int main(int argc, char *argv[]) { GSList *functions = NULL; struct funcstruct p; p.func = mult2; functions = g_slist_append(functions, &p); p = * (struct funcstruct *) functions->data; printf("%d\n", p.func(10)); return 0; } 

It's arguable that this is quite a bit of extra code to make a few warnings disappear, but I don't like my code to generate warnings. Also, the above are toy examples. In the real code I'm writing, it turns out to be quite useful to wrap the list of functions in a struct.

I'd be interested to hear if this is problematic or if there's a better way of doing it.

1 Comment

If you do it this way, you cannot use the list outside the scope of p, because the pointer to p is only valid in the scope of p. So your list would be pretty useless. And making the struct type was completely unnecessary. If you had simply declared p as int (* p) (int); it would work pretty much identically to what you have now.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.