Why can't I cast a function pointer to (void *)?

18,732

Solution 1

You can't fix the warning. In fact, in my opinion it should be a hard error since it's illegal to cast function pointers to other pointers because there are architectures out there today where this isn't just a violation of the C standard but an actual error that will make the code not work. Compilers allow it because many architectures get away with it even though those programs will crash badly on some other architectures. But it's not just a theoretical standard violation, it's something that causes real bugs.

For example on ia64 function pointers are (or at least used to be last time I looked) actually two values, both necessary to make function calls across shared libraries or a program and a shared library. Likewise, the common practice to cast and call function pointers to functions returning a value to a pointer to a function returning void because you know you'll ignore the return value anyway is also illegal on ia64 because that can lead to trap values leaking into registers causing crashes in some unrelated piece of code many instructions later.

Don't cast function pointers. Always have them match types. This is not just standards pedantry, it's an important best practice.

Solution 2

One solution is to add a level of indirection. This helps with lots of things. Instead of storing a pointer to a function, store a pointer to a struct storing a pointer to a function.

typedef struct
{
   void (*ptr)(void);
} Func;

Func vf = { voidfunc };

ptrlist[123] = &vf;

etc.

Solution 3

This is something that has long been broken in the C standard and has never been fixed -- there is no generic pointer type that can be used for pointers to functions and pointers to data.

Before the C89 standard, all C compilers allowed converting between pointers of different types, and char * was generally used as a generic pointer that might point to any data type or any function. C89 added void *, but put in a clause that only object pointers could be converted to void *, without ever defining what an object is. The POSIX standard fixes this issue by mandating that void * and function pointers are safely convertable back and forth. So much code exists that converts function pointers to void * and expects it to work properly. As a result, pretty much all C compilers still allow it, and still generate the correct code, as any compiler that did not would be rejected as unusable.

Strictly speaking, if you want to have a generic pointer in C, you need to define a union that can hold either a void * or a void (*)() and use an explicit cast of the function pointer to the correct function pointer type before calling it.

Solution 4

The language lawyering reason is "because C standard does not explicitly allow it." C11 6.3.2.3p1/p8

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.

8. A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

Notice that a function is not an object in C terminology, hence there is nothing that allows you to convert a pointer to a function to a pointer to void, hence the behaviour is undefined.

Castability to void * is a common extension though. C11 J.5 Common extensions 7:

J.5.7 Function pointer casts

1. A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).

2. A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4).

This is required by for example POSIX - POSIX has a function dlsym that returns void * but in fact it returns either a pointer to a function or a pointer to an object, depending of the type of the symbol resolved.


As to why this happens - nothing in C standard is undefined or unspecified if the implementations could agree on it. However there were and are platforms where the assumption that a void pointer and function pointer would be of the same width would really make things difficult. One of these is the 8086 16-bit real mode.


And what to use instead then? You can still cast any function pointer to another function pointer, so you can use a generic function pointer void (*)(void) everywhere. If you need both void * and a function pointer, you must use a struct or union or allocate void * to point to the function pointer, or ensure that your code only runs on platforms where J.5.7 is implemented ;)

void (*)() is recommended by some sources too, but right now it seems to trigger a warning in latest GCCs because it doesn't have a prototype.

Solution 5

As pointed out in other answers you shouldn't be allowed to assign a function pointer to an object pointer such as a void*. But you can safely assign a function pointer to any function pointer. Use reinterpret_cast in C++.

Let me give an example:

typedef void(*pFun)(void);
double increase(double a){return a+1.0;}

pFun ptrToFunc = reinterpret_cast<void(*)(void)>(increase);

the plain

pFun ptrToFunc = increase;

doesn't compile on several compilers.

Share:
18,732

Related videos on Youtube

sagittarian
Author by

sagittarian

Updated on June 06, 2022

Comments

  • sagittarian
    sagittarian almost 2 years

    I have a function that takes a string, an array of strings, and an array of pointers, and looks for the string in the array of strings, and returns the corresponding pointer from the array of pointers. Since I use this for several different things, the pointer array is declared as an array of (void *), and the caller should know what kind of pointers are actually there (and hence what kind of a pointer it gets back as the return value).

    When I pass in an array of function pointers, however, I get a warning when I compile with -Wpedantic:

    clang:

    test.c:40:8: warning: assigning to 'voidfunc' (aka 'void (*)(void)') from 'void *' converts
          between void pointer and function pointer [-Wpedantic]
    

    gcc:

    test.c:40:8: warning: ISO C forbids assignment between function pointer and ‘void *’ [-Wpedantic]
       fptr = find_ptr("quux", name_list, (void **)ptr_list,
    

    Here's a test file, which despite the warning does correctly print "quux":

    #include <stdio.h>
    #include <string.h>
    
    void foo(void)
    {
      puts("foo");
    }
    
    void bar(void)
    {
      puts("bar");
    }
    
    void quux(void)
    {
      puts("quux");
    }
    
    typedef void (* voidfunc)(void);
    
    voidfunc ptr_list[] = {foo, bar, quux};
    
    char *name_list[] = {"foo", "bar", "quux"};
    
    void *find_ptr(char *name, char *names[], void *ptrs[], int length)
    {
      int i;
    
      for (i = 0; i < length; i++) {
        if (strcmp(name, names[i]) == 0) {
          return ptrs[i];
        }
      }
      return NULL;
    }
    
    int main() {
      voidfunc fptr;
    
      fptr = find_ptr("quux", name_list, (void **)ptr_list,
                      sizeof(ptr_list) / sizeof(ptr_list[0]));
      fptr();
    
      return 0;
    }
    

    Is there any way to fix the warning, other than not compiling with -Wpedantic, or duplicating my find_ptr function, once for function pointers and once for non-function pointers? Is there a better way to achieve what I'm trying to do?

    • abligh
      abligh about 8 years
      IIRC the issue is that code pointers may not necessarily be the same size as data pointers. In most architectures they are. I will leave it to someone else to find the reference to the C standard. You can probably achieve the result you want by casting via uintptr_t and back.
    • erdeszt
      erdeszt about 8 years
      It's undefined behavior to cast function pointers to other pointer types because of the possible size mismatch mentioned by @abligh For more details see this question: stackoverflow.com/questions/13696918/…
    • alk
      alk about 8 years
      What keeps you from replacing void *ptrs[] by voidfunc * ptrs?
    • MikeC
      MikeC about 8 years
      Did you try a union of data pointers and function pointers?
    • alk
      alk about 8 years
      As you obviously do not want to call the function selected, but just return it, you want to solve this by an indirection, probably passing an array of indexes, referring to the entries of the array you are using for this current and unsolvable approach.
    • Peter - Reinstate Monica
      Peter - Reinstate Monica almost 8 years
      Having two parallel arrays with related information at the same index in the respective array is so 70s, i.e. BCPL! C introduced structures; I would put a string (key) and the properly typed function pointer (value) in a struct and have a single array of those structs. Sometime in the late 80s people started using associative arrays or dictionaries for that (in C++ the lookup and call would boil down to something like funcmap["quux"]()). This needs strong types; for functions with various signatures that can be difficult.
  • davmac
    davmac about 8 years
    You don't need a struct; you can just use a pointer-to-pointer.
  • HolyBlackCat
    HolyBlackCat about 8 years
    Great answer, but I have one question: Is it safe to store pointer to a (any) function inside of void (*f)() and cast it to a proper type when making a call?
  • Art
    Art about 8 years
    @HolyBlackCat From what I know about architectures that exist today, yes. From the point of view of the standard, no. From the point of view of future compatibility, I'd rather not. From the point of view of what company you'll be in when it breaks, go for it. A good way to see what you can get away with is to look at large projects. If for example the Linux kernel breaks when compilers get strict about something you can be pretty sure you'll hear about it and at least there'll be flags to disable it. Same goes for hardware architectures, few will build a CPU today that Linux can't run on.
  • n. m.
    n. m. about 8 years
    @davmac Yes, but you can add more stuff to the struct and build a poor man's closure.
  • sagittarian
    sagittarian about 8 years
    I don't want to declare the pointer array parameter as function pointers because I use the same function for other things too.
  • BeeOnRope
    BeeOnRope about 7 years
    This answer contradicts yours. In particular, it quotes the standard which indicates that function pointers can be cast to any other function pointer type and back again.
  • Art
    Art about 7 years
    @BeeOnRope It doesn't contradict. I just made a little bit too hard restriction (in parentheses). Edited it out since it wasn't too relevant. And clarified what I mean with pointers to functions returning a value being cast to void functions.
  • BeeOnRope
    BeeOnRope about 7 years
    Right well I meant it contradicts that one part about casting function pointers. If you could cast them at all it would be problematic, but as it is you can use void (*)() as kind of a void pointer for function pointers.
  • technosaurus
    technosaurus over 5 years
    Is it void (*)() or void* (*)()
  • Antti Haapala -- Слава Україні
    Antti Haapala -- Слава Україні over 5 years
    @technosaurus void (*)() is a pointer to a function taking unspecified arguments and returning void.
  • Andrew Henle
    Andrew Henle over 5 years
    The reason why the compiler forbids the conversion is that sizeof(void(*)(void)) can be different than sizeof(void*) That is one reason. That is not the reason. Read @Art's answer to this very question for another reason. Not only that, this answer is confusing at best, and perhaps even a strict aliasing violation, and certainly incomplete. What is voidfunc? How is the list populated so that your find_item() will work? And *(voidfunc*)find_item sure looks like a potential strict aliasing violation.
  • jpa
    jpa over 5 years
    @AndrewHenle This isn't actually casting a function pointer anywhere. It is casting a pointer to a function pointer, and the function pointers themselves are just data. As far as I know, pointer arithmetic using void* and char* doesn't violate strict aliasing in any way. They aren't even dereferenced here. I felt like it wouldn't make sense to copy-paste all of the code from the original question when I only modify one function - you can find the typedef for voidfunc etc. in the question.
  • Andrew Henle
    Andrew Henle over 5 years
    It is casting a pointer to a function pointer You can not do that in strictly-conforming C code. Read 6.3.2.3 Pointers. What you are proposing is not covered by any of the eight paragraphs there, and is therefore beyond the scope of the standard C language.
  • jpa
    jpa over 5 years
    @AndrewHenle You raise a valid point; I don't see why the standard doesn't seem to allow this, so I've asked it as a separate question stackoverflow.com/questions/53033813/… . I guess it boils down to whether a function pointer is "object type" or not (a function itself certainly isn't an object, but the pointer might be).
  • technosaurus
    technosaurus over 5 years
    @AnttiHaapala That was my point. void (*)() only works for functions that return void. void* (*)() works for a larger range of function return types.
  • Antti Haapala -- Слава Україні
    Antti Haapala -- Слава Україні over 5 years
    @technosaurus incorrect-o. You must always back-cast to the correct function-pointer type before calling it, no matter "how close" it looks.
  • Gabriel Ravier
    Gabriel Ravier over 4 years
    If the ia64 ABI mandates that function pointers are incompatible with object pointers, wouldn't that make ia64 ABI compliance and POSIX compliance incompatible requirements (as POSIX mandates that object pointers and functions pointers are compatible, as may be seen with functions such as dlsym)? Seems weird to me tbh
  • user426
    user426 over 2 years
    "This is something that has long been broken in the C standard and has never been fixed" is slightly inaccurate. This is a hardware standardisation limitation, not a language standard limitation.
  • user426
    user426 over 2 years
    Why not use a suggest a simpler C style cast, especially given this is a C question ? pFun ptrToFunc = (void(*)(void))increase;?
  • user426
    user426 over 2 years
    @AndrewHenle "perhaps even a strict aliasing violation". C : "hey, here's some strict aliasing rules, UB if you violate them, perf reasons.". Dev : "Hey compiler, you'll error out on violation right ?". Compiler : "...". Dev : "...Right ???".
  • pmor
    pmor over 2 years
    Can you please refer the C11 where the "initialization between function pointer and 'void *'" is forbidden?
  • Chris Dodd
    Chris Dodd over 2 years
    @pmor: 6.3.2.3 speficially lists out all the pointer conversions that are legal -- conversions between function pointers and object pointers are not listed, so are undefined.
  • Chris Dodd
    Chris Dodd over 2 years
    8086 16-bit mode has the issue of two different sizes of pointers, but the two sizes apply to both object pointers and function pointers equally. So for compilers that use near and far pointer qualifiers, a near object pointer is the same width as a near function pointer while a far pointer is similarly always the same width (though different from a near)
  • Antti Haapala -- Слава Україні
    Antti Haapala -- Слава Україні over 2 years
    @ChrisDodd Turbo C for example had several memory models, medium memory model meant that all object pointers were near, and code pointers (functions) were far, and the opposite model compact meant that all object pointers were far and function pointers near.
  • Ben Voigt
    Ben Voigt about 2 years
    @AndrewHenle "It is casting a pointer to a function pointer" is ambiguous. Is "to" the preposition associated with "casting" or with "pointer". You assumed the former, while in context the latter was meant, and is strictly conformant. Reworded slightly to remove the ambiguity: "There exists a pointer to a function pointer. I will cast it."
  • Andrew
    Andrew about 2 years
    Booooooooooooooo!!! Who are you to tell me what I should and shouldn't cast to void*?