I have read that converting a function pointer to a data pointer and vice versa works on most platforms but is not guaranteed to work. Why is this the case? Shouldn't both be simply addresses into main memory and therefore be compatible?
An architecture doesn't have to store code and data in the same memory. With a Harvard architecture, code and data are stored in completely different memory. Most architectures are Von Neumann architectures with code and data in the same memory but C doesn't limit itself to only certain types of architectures if at all possible.
CS != DS
). –
Teplitz VirtualProtect
, which allows you to mark regions of data as executable. –
Maletta Some computers have (had) separate address spaces for code and data. On such hardware it just doesn't work.
The language is designed not only for current desktop applications, but to allow it to be implemented on a large set of hardware.
It seems like the C language committee never intended void*
to be a pointer to function, they just wanted a generic pointer to objects.
The C99 Rationale says:
6.3.2.3 Pointers
C has now been implemented on a wide range of architectures. While some of these architectures feature uniform pointers which are the size of some integer type, maximally portable code cannot assume any necessary correspondence between different pointer types and the integer types. On some implementations, pointers can even be wider than any integer type.The use of
void*
(“pointer tovoid
”) as a generic object pointer type is an invention of the C89 Committee. Adoption of this type was stimulated by the desire to specify function prototype arguments that either quietly convert arbitrary pointers (as infread
) or complain if the argument type does not exactly match (as instrcmp
). Nothing is said about pointers to functions, which may be incommensurate with object pointers and/or integers.
Note Nothing is said about pointers to functions in the last paragraph. They might be different from other pointers, and the committee is aware of that.
void *
. –
Dumont void*
is compatible with function pointers? I thought ALL non-function pointers were incompatible with function pointers, including void*
–
Xanthous sizeof(void*) == sizeof( void(*)() )
would waste space in the case where function pointers and data pointers are different sizes. This was a common case in the 80's, when the first C standard was written. –
Softboiled void
pointers. –
Dumont void*
and back, but doesn't. Presumably since there is pretty much no support in C for treating functions as objects, there was no reason for the committee to add potential overhead to object pointers in order to let them hold function pointers portably. –
Cogency For those who remember MS-DOS, Windows 3.1 and older the answer is quite easy. All of these used to support several different memory models, with varying combinations of characteristics for code and data pointers.
So for instance for the Compact model (small code, large data):
sizeof(void *) > sizeof(void(*)())
and conversely in the Medium model (large code, small data):
sizeof(void *) < sizeof(void(*)())
In this case you didn't have separate storage for code and date but still couldn't convert between the two pointers (short of using non-standard __near and __far modifiers).
Additionally there's no guarantee that even if the pointers are the same size, that they point to the same thing - in the DOS Small memory model, both code and data used near pointers, but they pointed to different segments. So converting a function pointer to a data pointer wouldn't give you a pointer that had any relationship to the function at all, and hence there was no use for such a conversion.
int*
to a void*
give you a pointer that you can't really do anything with, but it's still useful to be able to perform the conversion. (This is because void*
can store any object pointer, so can be used for generic algorithms that don't need to know what type they hold. The same thing could be useful for function pointers as well, if it were allowed.) –
Tatiania int *
to void *
, the void *
is guaranteed to at least point to the same object as the original int *
did - so this is useful for generic algorithms that access the pointed-to object, like int n; memcpy(&n, src, sizeof n);
. In the case where converting a function pointer to a void *
doesn't yield a pointer pointing at the function, it isn't useful for such algorithms - the only thing you could do is convert the void *
back to a function pointer again, so you might as well just use a union
containing a void *
and function pointer. –
Teplitz void*
did point to the function, I suppose it would be a bad idea for people to pass it to memcpy
. :-P –
Tatiania void
. Conversion of a function pointer to void *
shall not alter the representation. A void *
value resulting from such a conversion can be converted back to the original function pointer type, using an explicit cast, without loss of information. Note: The ISO C standard does not require this, but it is required for POSIX conformance. –
Triode Pointers to void are supposed to be able to accommodate a pointer to any kind of data -- but not necessarily a pointer to a function. Some systems have different requirements for pointers to functions than pointers to data (e.g, there are DSPs with different addressing for data vs. code, medium model on MS-DOS used 32-bit pointers for code but only 16-bit pointers for data).
dlsym_function()
for returning function symbols, or dlsym()
would return a union {}
that contains both a void *
and a void (*)()
. –
Teplitz In addition to what is already said here, POSIX requires pointers to functions with external linkage to be convertible to void*
in the API of dlsym
:
The ISO C standard does not require that pointers to functions can be cast back and forth to pointers to data. Indeed, the ISO C standard does not require that an object of type
void*
can hold a pointer to a function. Implementations supporting the XSI extension, however, do require that an object of typevoid*
can hold a pointer to a function. The result of converting a pointer to a function into a pointer to another data type (exceptvoid*
) is still undefined, however. Note that compilers conforming to the ISO C standard are required to generate a warning if a conversion from avoid*
pointer to a function pointer is attempted as in:
fptr = (int (*)(int))dlsym(handle, "my_function");
Due to the problem noted here, a future version may either add a new function to return function pointers, or the current interface may be deprecated in favor of two new functions: one that returns data pointers and the other that returns function pointers.
In other words:
- ISO C standard does not require that pointers to functions can be cast back and forth to pointers to data. Meaning that such casts are well-formed C code, but with non-portable and undefined effects, hence the requirement for compiler warning.
- POSIX requires that for
dlsym
. - System V platform-specific ABIs (Mac OS, Linux, e.g. System V ABI for AMD64) in its ABI type
Pointer
definition require that both function and data pointers have the same specific size and alignment.
void*
and back . –
Snowblink void*
to be compatible with a function pointer, whereas POSIX does. –
Snowblink C++11 has a solution to the long-standing mismatch between C/C++ and POSIX with regard to dlsym()
. One can use reinterpret_cast
to convert a function pointer to/from a data pointer so long as the implementation supports this feature.
From the standard, 5.2.10 para. 8:
converting a function pointer to an object pointer type or vice versa is conditionally-supported.
1.3.5 defines "conditionally-supported" as a:
program construct that an implementation is not required to support.
-Werror
). A better (and non-UB) solution is to retrieve a pointer to the object returned by dlsym
(i.e. void**
) and convert that to a pointer to function pointer. Still implementation-defined but no longer cause for a warning/error. –
Weinrich dlsym
and GetProcAddress
to compile without warning. –
Pelagia -pedantic
) does warn. Again, no speculation possible. –
Weinrich Depending on the target architecture, code and data may be stored in fundamentally incompatible, physically distinct areas of memory.
void *
is large enough to hold any data pointer, but not necessarily any function pointer. –
Thorwald undefined doesn't necessarily mean not allowed, it can mean that the compiler implementor has more freedom to do it how they want.
For instance it may not be possible on some architectures - undefined allows them to still have a conforming 'C' library even if you can't do this.
Another solution:
Assuming POSIX guarantees function and data pointers to have the same size and representation (I can't find the text for this, but the example OP cited suggests they at least intended to make this requirement), the following should work:
double (*cosine)(double);
void *tmp;
handle = dlopen("libm.so", RTLD_LAZY);
tmp = dlsym(handle, "cos");
memcpy(&cosine, &tmp, sizeof cosine);
This avoids violating the aliasing rules by going through the char []
representation, which is allowed to alias all types.
Yet another approach:
union {
double (*fptr)(double);
void *dptr;
} u;
u.dptr = dlsym(handle, "cos");
cosine = u.fptr;
But I would recommend the memcpy
approach if you want absolutely 100% correct C.
They can be different types with different space requirements. Assigning to one can irreversibly slice the value of the pointer so that assigning back results in something different.
I believe they can be different types because the standard doesn't want to limit possible implementations that save space when it's not needed or when the size could cause the CPU to have to do extra crap to use it, etc...
The only truly portable solution is not to use dlsym
for functions, and instead use dlsym
to obtain a pointer to data that contains function pointers. For example, in your library:
struct module foo_module = {
.create = create_func,
.destroy = destroy_func,
.write = write_func,
/* ... */
};
and then in your application:
struct module *foo = dlsym(handle, "foo_module");
foo->create(/*...*/);
/* ... */
Incidentally, this is good design practice anyway, and makes it easy to support both dynamic loading via dlopen
and static linking all modules on systems that don't support dynamic linking, or where the user/system integrator does not want to use dynamic linking.
foo_module
structure (with unique names), you can simply create an extra file with an array of struct { const char *module_name; const struct module *module_funcs; }
and a simple function to search this table for the module you want to "load" and return the right pointer, then use this in place of dlopen
and dlsym
. –
Pappose A modern example of where function pointers can differ in size from data pointers: C++ class member function pointers
Directly quoted from https://blogs.msdn.microsoft.com/oldnewthing/20040209-00/?p=40713/
class Base1 { int b1; void Base1Method(); }; class Base2 { int b2; void Base2Method(); }; class Derived : public Base1, Base2 { int d; void DerivedMethod(); };
There are now two possible
this
pointers.A pointer to a member function of
Base1
can be used as a pointer to a member function ofDerived
, since they both use the samethis
pointer. But a pointer to a member function ofBase2
cannot be used as-is as a pointer to a member function ofDerived
, since thethis
pointer needs to be adjusted.There are many ways of solving this. Here's how the Visual Studio compiler decides to handle it:
A pointer to a member function of a multiply-inherited class is really a structure.
[Address of function] [Adjustor]
The size of a pointer-to-member-function of a class that uses multiple inheritance is the size of a pointer plus the size of a
size_t
.
tl;dr: When using multiple inheritance, a pointer to a member function may (depending on compiler, version, architecture, etc) actually be stored as
struct {
void * func;
size_t offset;
}
which is obviously larger than a void *
.
On most architectures, pointers to all normal data types have the same representation, so casting between data pointer types is a no-op.
However, it's conceivable that function pointers might require a different representation, perhaps they're larger than other pointers. If void* could hold function pointers, this would mean that void*'s representation would have to be the larger size. And all casts of data pointers to/from void* would have to perform this extra copy.
As someone mentioned, if you need this you can achieve it using a union. But most uses of void* are just for data, so it would be onerous to increase all their memory use just in case a function pointer needs to be stored.
I know that this hasn't been commented on since 2012, but I thought it would be useful to add that I do know an architecture that has very incompatible pointers for data and functions since a call on that architecture checks privilege and carries extra information. No amount of casting will help. It's The Mill.
© 2022 - 2024 — McMap. All rights reserved.
void
. Conversion of a function pointer tovoid *
shall not alter the representation. Avoid *
value resulting from such a conversion can be converted back to the original function pointer type, using an explicit cast, without loss of information. Note: The ISO C standard does not require this, but it is required for POSIX conformance. – Triodevoid*
conversions now applies only to values returned bydlsym()
. – Beagledlsym()
— note the end of the 'Application Usage' section where it says: Note that conversion from avoid *
pointer to a function pointer as in:fptr = (int (*)(int))dlsym(handle, "my_function");
is not defined by the ISO C standard. This standard requires this conversion to work correctly on conforming implementations. – Triode