51

我有一个 dlsym() 返回的 void 指针,我想调用 void 指针指向的函数。所以我通过强制转换进行类型转换:

void *gptr = dlsym(some symbol..) ;
typedef void (*fptr)();
fptr my_fptr = static_cast<fptr>(gptr) ;

我也尝试过reinterpret_cast,但没有运气,虽然 C cast 运算符似乎工作..

4

8 回答 8

60

在 C++98/03 中,不允许将 a直接转换void*为函数指针(不应使用任何类型转换进行编译)。C++0x 有条件地支持它(实现可以选择定义行为,如果确实定义了行为,那么它必须按照标准所说的去做。A ,如 C++98/03 标准所定义, 是为了指向对象而不是包含函数指针或成员指针。 void*

知道您正在做的事情在很大程度上依赖于实现,这里有一个选项应该long long在大多数平台上编译和工作(假设 32 位指针,用于 64 位),即使根据标准它显然是未定义的行为:

void *gptr = dlsym(some symbol..) ;
typedef void (*fptr)();
fptr my_fptr = reinterpret_cast<fptr>(reinterpret_cast<long>(gptr)) ;

这是另一个应该编译和工作的选项,但带有与上面相同的警告:

fptr my_ptr = 0;
reinterpret_cast<void*&>(my_ptr) = gptr; 

或者,在慢动作中...

// get the address which is an object pointer
void (**object_ptr)() = &my_ptr;  

// convert it to void** which is also an object pointer
void ** ppv = reinterpret_cast<void**>(object_ptr);

// assign the address in the memory cell named by 'gptr' 
// to the memory cell that is named by 'my_ptr' which is
// the same memory cell that is pointed to 
// by the memory cell that is named by 'ppv'
*ppv = gptr;  

它本质上利用了函数指针的地址是一个对象指针 ( void (**object_ptr)()) 的事实——因此我们可以使用它来将其转换为任何其他对象指针:reinterpret_cast例如 void**然后我们可以沿着地址返回(通过取消引用void**)到实际的函数指针,并将 gptr 的值存储在那里。

yuk - 绝不是定义明确的代码 - 但它应该做你期望它在大多数实现中做的事情。

于 2009-07-08T06:10:58.063 回答
19

请注意,C++11 允许这种转换,并且从 gcc 4.9 及更高版本开始,这种转换不会产生警告:https ://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869 。

请参阅 SO 讨论:

于 2016-05-01T08:21:58.277 回答
2

好吧,如果您知道参数列表是什么,那么只需 c-cast 它就非常简单。如上所述,它具有未定义的行为,但我一直在我自己的宠物项目事件处理程序中使用它,它似乎在 msvc 上工作得很好。
我可以将相同的 void* 强制转换为 _beginthread_proc_type 以使用 _beginthread 启动线程,这似乎也不会导致任何问题(尽管我真的不知道将参数发送到不需要任何函数的函数的后果,或者不向需要参数的函数发送参数,这似乎至少在我有限的测试中调用了函数/启动线程)

void somefunction(){
    std::cout <<"hi"<<std::endl;
}

void* function = (void*)&somefunction;
((void(__cdecl*)(void))(function)) ();

_beginthread((_beginthread_proc_type)function, 0, NULL);

我知道社区已经对宏产生了仇恨,但我在事件处理程序中使用宏来调用该函数。

#define call_voidstar_function(fc)     ((void(__cdecl*)(void))(fc)) ()
于 2020-11-05T05:35:51.720 回答
1

这在 Visual Studio 中编译而不使用 reinterpret cast:

void *ptr;
int (*func)(void) = (int(*)(void))ptr;
int num = func();
于 2011-11-20T01:51:55.323 回答
1

我发现了这个(有点难看)的解决方案。具有最高警告级别的 gcc 不会抱怨。此示例调用 dlsym()(返回 void*)并在函数指针中返回结果。

typedef void (*FUNPTR)();

FUNPTR fun_dlsym(void* handle, const char* name) {
    union {
        void* ptr;
        FUNPTR fptr;
    } u;
    u.ptr = dlsym(handle, name);
    return u.fptr;
}
于 2015-06-11T19:02:51.847 回答
0

人们可能会使用以下技术:

int (*fn)(int);
*(void **)(&fn) = dlsym(lib1, "function");
int result = (*fn)(3);

或者

fn = (int (*)(int))dlsym(lib1, "function");

编译:

g++ -Wall -pedantic -std=c++11
于 2018-08-08T21:45:23.283 回答
0

您可以强制dlsym转换为返回所需指针的函数,然后像这样调用它:

typedef void (*fptr)();
fptr my_fptr = reinterpret_cast<fptr (*)(void*, const char*)>(dlsym)(RTLD_DEFAULT, name);

PS。将函数指针转换为不同的函数指针然后调用它是未定义的行为(请参阅https://en.cppreference.com/w/cpp/language/reinterpret_cast中的第 7 点),因此最好将结果dlsym转换为uintptr_t和然后到所需的类型:

fptr my_fptr = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(dlsym(RTLD_DEFAULT, name)));
于 2019-06-16T21:11:37.063 回答
-4

这可能会对您有所帮助。它打印“你好”。

#include <iostream>

void hello()
{
  std::cout << "Hello" << std::endl;
}

int main() {
  typedef void (*fptr)();
  fptr gptr = (fptr) (void *) &hello;
  gptr();
}

或者你可以这样做:

fptr gptr = reinterpret_cast<fptr>( (void *) &hello);

其中 &hello 被 dlsym 命令替换。

于 2009-07-08T06:15:48.410 回答