将指针转换为整数

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 位机器,sizeofsize_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_tintptr_t 类型。 (另请参阅 cppreference integer types for C99)。

这些类型在 &lt;stdint.h&gt; 中定义(对于 C99)和命名空间 std(对于 C++11)在 &lt;cstdint&gt; 中定义(参见 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 的唯一答案 如果您打算包含 ,您可能还想使用 std::uintptr_t 代替。 太棒了...演员阵容正是我想要的。如果我们被告知使用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_tuintptr_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 适当的解决方法是切换到&lt;cstdint&gt;,或者如果您下载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 &lt;stdint.h&gt; 是“解决方案”。也就是说,我建议,部分答案,但不是全部答案。您还需要查看使用 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 * 参数的值(转换后的),请仔细查看&lt;inttypes.h&gt; 标头(而不是stdint.hinttypes.h 提供stdint.h 的服务,这是不寻常的,但 C99 标准说 [t]他的标头 &lt;inttypes.h&gt; 包括标头 &lt;stdint.h&gt; 并扩展它 托管实现提供的附加工具)并在格式字符串中使用 PRIxxx 宏。

此外,我的 cmets 严格适用于 C 而不是 C++,但您的代码位于 C++ 的子集中,可在 C 和 C++ 之间移植。我的 cmets 申请的机会相当大。

【讨论】:

我认为您错过了我的问题的重点。该代码将整数的值存储在指针中。而那部分代码则相反(例如,提取写为 指针的整数的值)。 @PierreBdR 尽管如此,他提出了一个非常有效的观点。查看使用有符号 int 但用于大小并认为可以将其更改为无符号的代码(包括编译器警告它时)并不总是那么简单。不幸的是,事情并不总是那么简单。您必须明确地查看每种情况,除非您想引起潜在的错误 - 以及潜在的错误。【参考方案6】:
    #include &lt;stdint.h&gt; 使用在包含的标准头文件中定义的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,始终在&lt;cstdint&gt; 中定义。

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


【讨论】:

以上是关于将指针转换为整数的主要内容,如果未能解决你的问题,请参考以下文章

将指针字符串转换为整数

C语言,将一个整数转化为字符串再与另一字符串连接。(用指针)

gcc警告分配使指针从整数而不进行强制转换[重复]

易语言指针转换及调用方法

警告:从/到不同大小的整数转换为/从指针转换

指向整数转换的不兼容指针将“SystemSoundID”传递给“SystemSoundID”类型的参数