Why are function pointers and data pointers incomp

2019-01-02 17:49发布

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?

14条回答
零度萤火
2楼-- · 2019-01-02 18:21

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.

查看更多
浮光初槿花落
3楼-- · 2019-01-02 18:24

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.

查看更多
其实,你不懂
4楼-- · 2019-01-02 18:24

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".

查看更多
长期被迫恋爱
5楼-- · 2019-01-02 18:26

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 of Derived, since they both use the same this pointer. But a pointer to a member function of Base2 cannot be used as-is as a pointer to a member function of Derived, since the this 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 *.

查看更多
浅入江南
6楼-- · 2019-01-02 18:30

In addition to what is already said here, it is interesting to look at POSIX 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 type void * can hold a pointer to a function. The result of converting a pointer to a function into a pointer to another data type (except void *) is still undefined, however. Note that compilers conforming to the ISO C standard are required to generate a warning if a conversion from a void * 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.

查看更多
何处买醉
7楼-- · 2019-01-02 18:30

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.

查看更多
登录 后发表回答