【问题标题】:Function pointers casting in C++C++ 中的函数指针转换
【发布时间】:2023-03-29 23:12:01
【问题描述】:

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

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

我也尝试过reinterpret_cast,但没有运气,尽管 C 转换运算符似乎可以工作..

【问题讨论】:

  • 我认为将函数指针转换为 void* 原本是个坏主意。从 dlsym 返回指向函数的指针有问题吗?

标签: c++ casting function-pointers


【解决方案1】:

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

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

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 - 绝不是定义明确的代码 - 但它应该可以满足您对大多数实现的期望。

【讨论】:

  • 我希望是这样 - C++ 转换符合标准,C 转换向后兼容 POSIX 共享库调用的要求。
  • 作为旁注,在中间转换中使用的类型的更好选择可能是size_t - 它通常足够大以适应任何平台上的指针,即使这不是保证。更好的是,在可用的地方使用&lt;stdint.h&gt;/&lt;cstdint&gt; 标头和intptr_t typedef(C99、C++TR1、C++0x)。
  • “有条件支持”的措辞实际上是在考虑 dlsym() 行为的情况下发明的 - 大约在 2001 年,人们注意到用于 POSIXy 系统的真正 C++ 编译器都接受了这种转换。
  • @MSalters - 感谢背景 :) - 这是可能已经开始的 DR 的链接:open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#195
  • 如果你想要一种更便携的方式,你总是可以在 C 中围绕特定的 dlsym 编写一个包装器,它确实返回一个函数指针并从 C++ 调用该包装器。
【解决方案2】:
【解决方案3】:

好吧,如果您知道参数列表是什么,那么只需 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)) ()

【讨论】:

    【解决方案4】:

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

    void *ptr;
    int (*func)(void) = (int(*)(void))ptr;
    int num = func();
    

    【讨论】:

    • 可能编译,但导致未定义的行为(如 C 规范所述)。
    • 真的没有reinterpret_cast吗?编译器会选择哪个演员表?
    • 你正在做一个 c 风格的演员表,在这种情况下这实际上是重新解释演员表。
    【解决方案5】:

    我发现了这个(有点难看的)解决方案。 具有最高警告级别的 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;
    }
    

    【讨论】:

    【解决方案6】:

    可能会使用以下技术:

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

    或者

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

    编译:

    g++ -Wall -pedantic -std=c++11
    

    【讨论】:

      【解决方案7】:

      您可以将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)));
      

      【讨论】:

        【解决方案8】:

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

        #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 命令替换。

        【讨论】:

        • 如果有帮助我会惊讶
        • 有效的原因是你没有通过void *指针。
        • 编辑后他是,对吧?并且代码似乎有效。 (虽然我不是专家,所以也许这会起作用,但实际上是未定义的?)
        猜你喜欢
        • 2021-03-23
        • 2011-07-31
        • 2012-11-21
        • 1970-01-01
        • 2013-11-03
        • 2015-11-03
        • 2012-12-17
        • 2016-06-03
        相关资源
        最近更新 更多