25

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();
Tomwa ivory
  • 271
  • 1
  • 3
  • 7
  • 3
    How about not assigning a void pointer to a function pointer? – Robert Harvey Jan 03 '13 at 06: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. – Basile Starynkevitch Jan 03 '13 at 06:48
  • What does GetPointer() return? Is that second line where you get the error? – Jonathan Wood Jan 03 '13 at 06:50
  • 8
    @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) – Basile Starynkevitch Jan 03 '13 at 06:50
  • Anyway this whole function vs. data pointer concept is brain dead, it all boils down to `unsigned long`s, why bother? (Rhetorical question towards the standard comittee) –  Jan 03 '13 at 06:53
  • 1
    @JonathanWood GetPointer returns a void* I failed to note that in my original post. – Tomwa ivory Jan 03 '13 at 06:54
  • I see. Then that's not a valid assignment. You can't assign `void *` to `void *(*p)()`. Did you try typecasting it? – Jonathan Wood Jan 03 '13 at 06:58
  • 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. –  Jan 03 '13 at 07:01
  • @JonathanWood The program compiles and runs and everything works (Well except for the warning), attempting to cast the return of GetResult to void*()() yields an error stating "Cast specifies function type" – Tomwa ivory Jan 03 '13 at 07:04
  • That's not a valid cast. I'm not sure the exact syntax off the top of my head but you are missing the function pointer `*`. You're only including the return value `*`. – Jonathan Wood Jan 03 '13 at 16:17

3 Answers3

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]);
}
Weinan Li
  • 337
  • 2
  • 6
  • 3
    @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. – mtk Oct 31 '17 at 13:55
  • 3
    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. – Paul Groke Jul 31 '18 at 13:57
  • So confusing and unclear. What are the purposes/functions of all of: `dlfcn.h`, `dlsym`, `libHandle`, `void **`? – WhyWhat Dec 29 '22 at 11:27
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.)

  • 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. – Tomwa ivory Jan 03 '13 at 06:57
  • @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 –  Jan 03 '13 at 07:00
  • Thanks that's good to know, I can't fix the language just do my best to code around it :) – Tomwa ivory Jan 03 '13 at 07:07
  • @Tomwaivory Yes, basically :) Until it works *with a reasonable explanation,* there's no problem. Of course I'm not talking about UBs like "i++ + ++i", but still... –  Jan 03 '13 at 07:09
  • 5
    "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 Jan 03 '13 at 19:44
  • 1
    @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. –  Jan 03 '13 at 20:22
  • This is ridiculous. Couldn't they come up with a _portable_ type that can hold both a `void*` and a `void (*)(void)`? I want to know what kind of system can't hold a value large enough to be downcasted to either? – James M. Lay Jul 14 '22 at 21:09
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.

Nathan Geffen
  • 439
  • 1
  • 6
  • 6
  • 1
    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. – newacct Oct 22 '13 at 01:07