在 C++ 中正确地将 `void*` 转换为整数

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

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

不幸的是,以前编写此代码的人决定通过将整数转换为空指针 ((void*)val) 来将整数传递给这些回调。

我现在正在清理这个烂摊子,我正在尝试确定 "proper" 将整数 to/from 转换为 void* 的方法。不幸的是,修复 void 指针的 use 在某种程度上超出了我在这里能够做的返工的范围。

现在,我正在执行两次转换以将 from/to 转换为空指针:

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 一起工作,但我正试图摆脱它以解决可维护性和与错误相关的问题,所以我正在尝试这样做 "properly",例如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* 的整数值,并且一旦我有一个整数值,我就可以截断它而不会导致编译器抱怨。

考虑到我不得不通过 void 指针推送数据,这是正确的,甚至是明智的方法吗?

"Is this the correct, or even a sane approach given I'm stuck having to push data through a void pointer?"

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

I think using intptr_t is the correct intermediate here, since it's guaranteed to be large enough to contain the integral value of the void*, and once I have an integer value I can then truncate it without causing the compiler to complain.

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


在与 API 交互时偶然发现这个问题并不少见,它们提供 回调 ,让您传递 user-data,它们将透明地处理,并且永远不会被触及,除了你的入口点 1.

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


1) 这种情况的典型例子是pthread_create()函数

您别无选择,只能在此处使用 static 和 reinterpret cast。强制转换为 int 会导致精度损失,这从来都不是理想的情况。始终最好避免显式转换,因为迟早要转换的内容可能会发生变化,届时将不会有编译器警告。但在这种情况下,你别无选择是可以理解的。或者你呢?

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

I think using intptr_t is the correct intermediate here, since it's guaranteed to be large enough to contain the integral value of the void*, and once I have an integer value I can then truncate it without causing the compiler to complain.

是的,正如您提到的那样,这是正确的中间类型。到现在为止,如果您的实现不提供它,您可能遇到的问题不仅仅是缺少 typedef。

Is this the correct, or even a sane approach given I'm stuck having to push data through a void pointer?

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

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

根据您的问题,我假设您在某个库中调用一个函数,将其传递给 void*,并且在稍后的某个时间点,它调用您的一个函数,并同样传递给它void*.

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

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

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);
}

您应该使用哪一个取决于您需要对数据做什么,以及修改数据的能力在将来是否有用。