Microsoft extensions to C and C++:
To perform the same cast and also maintain ANSI compatibility, you can cast the function pointer to a
uintptr_tbefore you cast it to a data pointer:int ( * pfunc ) (); int *pdata; pdata = ( int * ) (uintptr_t) pfunc;
Rationale for C, Revision 5.10, April-2003:
Even with an explicit cast, it is invalid to convert a function pointer to an object pointer or a pointer to void, or vice versa.
C11:
7.20.1.4 Integer types capable of holding object pointers
Does it mean that pdata = ( int * ) (uintptr_t) pfunc; in invalid?
As Steve Summit says:
The C standard is written to assume that pointers to different object types, and especially pointers to function as opposed to object types, might have different representations.
While pdata = ( int * ) pfunc; leads to UB, it seems that pdata = ( int * ) (uintptr_t) pfunc; leads to IB. This is because "Any pointer type may be converted to an integer type" and "An integer may be converted to any pointer type" and uintptr_t is integer type.
Given the definitions
int (*pfunc)();
int *pdata;
, the assignments
pdata = (int *)pfunc;
pdata = (int *)(uintptr_t)pfunc;
are, IMO, equivalent. On a platform where data pointers are of the same size as, or larger than, function pointers, both assignments will work as desired. But on a platform where data pointers are smaller than function pointers, both assignments will inevitably scrape off some of the bits of the function pointer, resulting in a data pointer which can not be converted back to the original function pointer later.
In particular, I believe that both assignments are equivalent despite the presence of the (uintptr_t) cast in the second one. I believe that cast accomplishes precisely nothing.
On a platform where data pointers are smaller than function pointers, and where type uintptr_t is of the same size as data pointers, in the assignment
pdata = (int *)(uintptr_t)pfunc;
, the cast to (uintptr_t) will scrape off some of the bits of pfunc's value.
On a platform where data pointers are smaller than function pointers, and where type uintptr_t is of the same size as function pointers, in the assignment
pdata = (int *)(uintptr_t)pfunc;
, the cast to (int *) will scrape off some of the bits of pfunc's value.
In both cases pdata will end up with only some fraction of pfunc's original value.
(Here I disregard the possibility of architectures with padding bits or the like. On some bizarre, hypothetical platform where function pointers are larger than data pointers, but the extra bits are always 0, both assignments would again work.)
(I've also disregarded the possibility that int * is a different size than void *. I'm not sure whether that would affect the answer, whether a "detour" via void * is more or less un- or necessary when attempting a conversion from int (*)() to int *.)
Casting to uintptr_t only works if this type is defined, which may not be the case on legacy systems using ancient compilers. Note however that uintptr_t must be large enough for any object pointer, especially char * or void *, but may be smaller than function pointers. Such architectures are rare today and Microsoft compilers probably no longer support them, but they were common place in the 16-bit world (MS/DOS, Windows 1, 2 and 3.x) where the medium model had 32-bit segmented code pointers and 16-bit data pointers.
Note also that the C Standard allows for int * and void * to have a different size and representation albeit no Microsoft compiler supports such exotic targets.
On current systems with modern compilers, all data pointers and code pointers have almost always the same size and representation. This is actually a requirement for POSIX compatibility, so the recommendation to use an intermediary cast to (uintptr_t) is valid and effective.
For complete portability, if the goal is to pass a function pointer via an opaque void *, you can always allocate an object of the proper function pointer type, initialize it with pfunc and pass its address:
// setting up the void *
int (*pfunc)();
void *pdata = malloc(sizeof pfunc);
memcpy(pdata, &pfunc, sizeof pfunc);
// using the void *
int (**ppfunc)() = pdata;
(*ppfunc)(); // equivalent to (**ppfunc)();
Is conversion of a function pointer to a
uintptr_t / intptr_tinvalid?
No. It may be valid. It may be undefined behavior.
Conversion of a function pointer to ìnt* is not defined. Nor to any object pointer. Nor to void *.
pdata = ( int * ) pfunc; is undefined behavior.
Conversion of a function pointer to an integer type is allowed, with restrictions:
Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type. C17dr 6.3.2.3 6
Also integer to a pointer type is allowed.
An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation. C17dr 6.3.2.3 6
void * to integer to void * is defined. Object pointer to/from void* is defined. Then the optional (u)intptr_t types are sufficient for round-trip success. Yet we are concerned about a function pointer. Often enough function pointers are wider than an int *.
Thus converting a function pointer to int * only makes sense through an integer type, wider the better.
VS may recommend through the optional type uintptr_t and is likely sufficient if information is lossless on other platforms. Yet uintmax_t may afford less loss of information, especially in the function pointer to integer step, so I pedantically suggest:
pdata = ( int * ) (uintmax_t) pfunc;
Regardless of the steps taken, code is likely to become implementation specific and deserves guards.
#ifdef this && that
pdata = ( int * ) (uintmax_t) pfunc;
#else
#error TBD code
#endif