正确地将“void*”转换为 C++ 中的整数

Properly casting a `void*` to an integer in C++

本文关键字:C++ 整数 转换 void 正确地      更新时间:2023-10-16

我正在处理一些使用外部库的代码,您可以在其中通过void*值将值传递给回调。

不幸的是,处理此代码的上一个人员决定通过将整数转换为 void 指针 ( (void*)val 来将整数传递给这些回调。

我现在正在努力清理这个烂摊子,我正在尝试确定将整数转换为/从void*转换的"正确"方法。不幸的是,修复 void 指针的使用有点超出了我在这里能够做的返工范围。

现在,我正在做两个转换来转换/转换为空指针:

static_cast<int>(reinterpret_cast<intptr_t>(void_p))

reinterpret_cast<void *>(static_cast<intptr_t>(dat_val))

由于我在 64 位机器上,直接转换 ( (int)void_p ) 会导致错误:

error: cast from 'void*' to 'int' loses precision [-fpermissive]

原始实现确实适用于-fpermissive,但是由于可维护性和与错误相关的问题,我试图摆脱这一点,因此我正在尝试"正确"执行此操作,例如c ++强制转换。

直接转换为 int ( static_cast<int>(void_p) ) 失败 ( error: invalid static_cast from type 'void*' to type 'int' )。我对reinterpret_cast的理解是,它基本上只是导致编译器将相关值的地址视为强制转换为数据类型,而不实际发出任何机器代码,因此将int直接转换为void*将是一个坏主意,因为void*大于int(分别为 4/8 字节)。

我认为在这里使用 intptr_t 是正确的中间人,因为它保证足够大以包含void*的整数值,一旦我有一个整数值,我就可以截断它而不会导致编译器抱怨。

这是正确的,甚至是理智的方法,因为我不得不通过空指针推送数据?

我认为在这里使用 intptr_t 是正确的中间人,因为它保证足够大以包含void*的整数值,一旦我有一个整数值,我就可以截断它而不会导致编译器抱怨。

是的,由于您提到的原因,这是正确的中间类型。到目前为止,如果你的实现没有提供它,你可能有更多的问题,而不仅仅是缺少 typedef。

这是正确的,甚至是理智的方法,因为我不得不通过空指针推送数据?

是的,

考虑到限制,这是非常理智的。
您可以考虑检查值是否合适,而不是在调试模式下从void*中解压缩时简单地截断它,甚至使该整数的所有进一步处理都使用intptr而不是int以避免截断。

您还可以考虑通过该参数将指针推送到实际int而不是int本身。请注意,效率较低,并且会让您面临终身问题。

根据您的问题,我假设您在某个库中调用一个函数,void*传递它,然后在稍后的某个时间点,它调用您的一个函数,以相同的void*传递它。

基本上有两种可能的方法可以做到这一点;第一种是通过显式强制转换,如您在当前代码中所示。

另一个,重复数据删除器提到,效率稍低,但允许您保持对数据的控制,并可能在调用库函数和调用回调函数之间修改数据。这可以通过类似于下面的代码来实现:

void callbackFunction(void* dataPtr){
    int data = *(int*)dataPtr;
    /* DO SOMETHING WITH data */
    delete dataPtr;
}
void callLibraryFunction(int dataToPass){
    int* ptrToPass = new int(dataToPass);
    libraryFunction(ptrToPass,callbackFunction);
}

应该使用哪一个取决于您需要对数据执行的操作,以及修改数据的能力将来是否有用。

"这是正确的,还是一个理智的方法,因为我不得不通过一个空白指针推送数据?"

好吧,关于正确理智,这是值得商榷的,特别是如果您是在界面中void*的代码的作者。

我认为在这里使用 intptr_t 是正确的中间人,因为它保证足够大以包含 void* 的整数值,一旦我有一个整数值,我就可以截断它而不会导致编译器抱怨。

是的,这是与reinterpret_cast<intptr_t>一起使用的正确类型,但您需要确定,intptr_t指针类型已传入,并且地址有效且不会超出范围。


在与 c API 交互时,偶然发现这个问题并不罕见,这些回允许您传入用户数据,这些数据将由它们透明地处理,并且永远不会被触及,除了您的入口点1

因此,它由客户端代码确定如何安全地重新解释void*


1) 这种情况的一个经典例子是pthread_create()函数

你别无选择,只能在这里使用静态和重新解释强制转换。转换为 int 将导致精度损失,这从来都不是理想的。最好始终避免显式转换,因为迟早要转换的内容可能会更改,并且不会有编译器警告。但在这种情况下,您可以理解地别无选择。还是你?

您可以将这边的回调定义更改为 intptr_t 或 long int 而不是 void*,然后它应该可以工作,并且您不必进行任何类型转换......