将指针转换为整数
Posted
技术标签:
【中文标题】将指针转换为整数【英文标题】:Converting a pointer into an integer 【发布时间】:2010-09-14 06:17:19 【问题描述】:我正在尝试使现有代码适应 64 位机器。主要问题是在一个函数中,前面的编码器使用了一个 void* 参数,该参数在函数本身中被转换为合适的类型。一个简短的例子:
void function(MESSAGE_ID id, void* param)
if(id == FOO)
int real_param = (int)param;
// ...
当然,在 64 位机器上,我得到了错误:
error: cast from 'void*' to 'int' loses precision
我想更正此问题,使其在 32 位机器上仍能正常工作,并且尽可能干净。有什么想法吗?
【问题讨论】:
我知道这是在挖掘一个旧帖子,但似乎接受的答案并不完全正确。size_t
不工作的一个具体例子是 i386 分段内存。尽管是 32 位机器,sizeof
为 size_t
返回 2
。 Alex answer below 似乎是正确的。亚历克斯的回答和uintptr_t
几乎在任何地方都有效,而且它现在是标准的。它提供了 C++11 处理,甚至还提供了 C++03 标头保护。
【参考方案1】:
我会说这是现代 C++ 方式。
#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);
编辑:
整数的正确类型
所以将指针存储为整数的正确方法是使用uintptr_t
或intptr_t
类型。 (另请参阅 cppreference integer types for C99)。
这些类型在 <stdint.h>
中定义(对于 C99)和命名空间 std
(对于 C++11)在 <cstdint>
中定义(参见 integer types for C++)。
C++11(及更高版本)版本
#include <cstdint>
std::uintptr_t i;
C++03版
extern "C"
#include <stdint.h>
uintptr_t i;
C99版
#include <stdint.h>
uintptr_t i;
正确的转换操作符
在 C 中只有一个转换,并且在 C++ 中使用 C 转换是不受欢迎的(所以不要在 C++ 中使用它)。在 C++ 中有不同的转换。 reinterpret_cast
是此转换的正确转换(另请参阅 here)。
C++11 版本
auto i = reinterpret_cast<std::uintptr_t>(p);
C++03版
uintptr_t i = reinterpret_cast<uintptr_t>(p);
C 版
uintptr_t i = (uintptr_t)p; // C Version
相关问题
What is uintptr_t data type【讨论】:
正确提及 reinterpret_cast 的唯一答案 如果您打算包含uintptr_t
而不是size_t
,那么为什么它需要reinterpret_cast
?看起来应该是一个简单的static_cast
,因为标准专门提供了兼容的数据类型......
@jww 阅读:en.cppreference.com/w/cpp/language/static_cast 我的理解是static_cast
可能会转换类型,或者如果它是指针,则可能会在类型需要时进行指针调整。 reinterpret_cast
实际上只是改变了底层内存模式的类型(没有突变)。澄清一下:static_cast
在这里的行为确实相同。
这应该被标记为选择的答案,因为它提供了如何在 C 和 C++ 中转换的所有细节。【参考方案2】:
使用intptr_t
和uintptr_t
。
为确保它以可移植的方式定义,您可以使用如下代码:
#if defined(__BORLANDC__)
typedef unsigned char uint8_t;
typedef __int64 int64_t;
typedef unsigned long uintptr_t;
#elif defined(_MSC_VER)
typedef unsigned char uint8_t;
typedef __int64 int64_t;
#else
#include <stdint.h>
#endif
只需将其放在某个 .h 文件中并包含在您需要的任何地方。
或者,您可以从here 下载Microsoft 版本的stdint.h
文件,或使用here 的便携式文件。
【讨论】:
请参阅***.com/questions/126279/… 了解有关如何获取适用于 MSVC(可能还有 Borland)的 stdint.h 的信息。 两个链接都坏了! 这个答案与 C 有关,但语言被标记为 C++ 所以它不是我想要的答案。 @HaSeeBMiR 适当的解决方法是切换到<cstdint>
,或者如果您下载stdint.h
,则下载相应的cstdint
。
@HaSeeBMiR 答案与 C 而不是 C++ 相关的唯一原因是它使用 C 头文件而不是等效的 C++ 头文件。 C 预处理器是 C++ 的一部分,cstdint
是 C++ 标准的一部分,在那里定义的所有类型名称也是如此。它确实适用于指定的标签。 ...我不同意手动定义类型,但在使用不这样做的编译器时可能有必要。【参考方案3】:
'size_t' 和 'ptrdiff_t' 需要匹配您的架构(无论它是什么)。因此,我认为与其使用'int',不如使用'size_t',在64位系统上应该是64位类型。
这个讨论unsigned int vs size_t 更详细一点。
【讨论】:
虽然 size_t 通常大到足以容纳一个指针,但不一定如此。最好找到一个 stdint.h 头文件(如果你的编译器还没有)并使用 uintptr_t。 不幸的是,size_t
的唯一约束是它必须保存任何sizeof()
的结果。这不一定使它在 x64 上成为 64 位。 see also
size_t
可以安全地存储非成员指针的值。见en.cppreference.com/w/cpp/types/size_t。
@AndyJost 不,它不能。甚至您自己的链接也证实了这一点。
@YoYoYonnY:“在许多平台上(具有分段寻址的系统除外)std::size_t 可以安全地存储任何非成员指针的值,在这种情况下它与 std:: 同义uintptr_t。” - 你在说什么?【参考方案4】:
使用uintptr_t
作为整数类型。
【讨论】:
【参考方案5】:一些答案指出uintptr_t
和#include <stdint.h>
是“解决方案”。也就是说,我建议,部分答案,但不是全部答案。您还需要查看使用 FOO 的消息 ID 调用函数的位置。
考虑这段代码并编译:
$ cat kk.c
#include <stdio.h>
static void function(int n, void *p)
unsigned long z = *(unsigned long *)p;
printf("%d - %lu\n", n, z);
int main(void)
function(1, 2);
return(0);
$ rmk kk
gcc -m64 -g -O -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith \
-Wcast-qual -Wstrict-prototypes -Wmissing-prototypes \
-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE kk.c -o kk
kk.c: In function 'main':
kk.c:10: warning: passing argument 2 of 'func' makes pointer from integer without a cast
$
您会发现调用位置(在main()
)存在问题——将整数转换为没有强制转换的指针。您将需要分析您的 function()
的所有用法,以查看值是如何传递给它的。如果调用被编写,我的function()
中的代码将起作用:
unsigned long i = 0x2341;
function(1, &i);
由于您的可能写得不同,您需要检查调用函数的点,以确保使用所示值是有意义的。别忘了,你可能会发现一个潜在的错误。
另外,如果您要格式化void *
参数的值(转换后的),请仔细查看<inttypes.h>
标头(而不是stdint.h
— inttypes.h
提供stdint.h
的服务,这是不寻常的,但 C99 标准说 [t]他的标头 <inttypes.h>
包括标头 <stdint.h>
并扩展它
托管实现提供的附加工具)并在格式字符串中使用 PRIxxx 宏。
此外,我的 cmets 严格适用于 C 而不是 C++,但您的代码位于 C++ 的子集中,可在 C 和 C++ 之间移植。我的 cmets 申请的机会相当大。
【讨论】:
我认为您错过了我的问题的重点。该代码将整数的值存储在指针中。而那部分代码则相反(例如,提取写为 为 指针的整数的值)。 @PierreBdR 尽管如此,他提出了一个非常有效的观点。查看使用有符号 int 但用于大小并认为可以将其更改为无符号的代码(包括编译器警告它时)并不总是那么简单。不幸的是,事情并不总是那么简单。您必须明确地查看每种情况,除非您想引起潜在的错误 - 以及潜在的错误。【参考方案6】:#include <stdint.h>
使用在包含的标准头文件中定义的uintptr_t
标准类型。
【讨论】:
【参考方案7】:我在研究SQLite的源代码时遇到了这个问题。
在sqliteInt.h中有一段代码定义了整数和指针之间的宏转换。作者做了一个很好的陈述,首先指出它应该是一个依赖于编译器的问题,然后实施了解决方案来解释大多数流行的编译器。
#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
#else /* Generates a warning - but it always works */
# define SQLITE_INT_TO_PTR(X) ((void*)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(X))
#endif
这里是评论的引用以获取更多详细信息:
/*
** The following macros are used to cast pointers to integers and
** integers to pointers. The way you do this varies from one compiler
** to the next, so we have developed the following set of #if statements
** to generate appropriate macros for a wide range of compilers.
**
** The correct "ANSI" way to do this is to use the intptr_t type.
** Unfortunately, that typedef is not available on all compilers, or
** if it is available, it requires an #include of specific headers
** that vary from one machine to the next.
**
** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
** So we have to define the macros in different ways depending on the
** compiler.
*/
功劳归于提交者。
【讨论】:
【参考方案8】:最好的办法是避免从指针类型转换为非指针类型。 但是,在您的情况下,这显然是不可能的。
正如大家所说,uintptr_t 是你应该使用的。
link 提供了有关转换为 64 位代码的详细信息。
comp.std.c上也有很好的讨论
【讨论】:
【参考方案9】:我认为 void* 在这种情况下的“含义”是一个通用句柄。 它不是指向值的指针,而是值本身。 (这恰好是 C 和 C++ 程序员使用 void* 的方式。)
如果是整数值,最好在整数范围内!
这里很容易渲染成整数:
int x = (char*)p - (char*)0;
它应该只给出警告。
【讨论】:
【参考方案10】:由于uintptr_t
是not guaranteed to be there in C++/C++11,如果这是一种单向转换,您可以考虑uintmax_t
,始终在<cstdint>
中定义。
auto real_param = reinterpret_cast<uintmax_t>(param);
为了安全起见,可以在代码的任何地方添加断言:
static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
"No suitable integer type for conversion from pointer type");
【讨论】:
如果您没有 uintptr_t,那么 uintmax_t 也不是答案:没有保证可以在其中存储指针的值!可能没有整数类型可以做到这一点。【参考方案11】:对于 C++11,不管它的价值,假设你没有任何标题,然后定义:
template<bool B, class T, class F> struct cond typedef T type; ;
template<class T, class F> struct cond<false, T, F> typedef F type;;
static constexpr unsigned int PS = sizeof (void *);
using uintptr_type = typename cond<
PS==sizeof(unsigned short), unsigned short ,
typename cond<
PS==sizeof(unsigned int), unsigned int,
typename cond<
PS==sizeof(unsigned long), unsigned long, unsigned long long>::type>::type>::type;
之后,您可以执行以下操作:
static uintptr_type ptr_to_int(const void *pointer)
return reinterpret_cast<uintptr_type>(pointer);
static void *int_to_ptr(uintptr_type integer)
return reinterpret_cast<void *>(integer);
【讨论】:
以上是关于将指针转换为整数的主要内容,如果未能解决你的问题,请参考以下文章