将指针转换为整数


85

我正在尝试将现有代码改编为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位计算机上且尽可能干净地工作。任何的想法 ?


5
我知道这是在挖掘旧帖子,但似乎已被接受的答案不太正确。size_t无法运行的一个具体示例是i386分段内存。虽然32位机,sizeof返回2size_t以下Alex答案似乎正确。亚历克斯(Alex)的答案并uintptr_t适用于几乎所有地方以及现在的标准。它提供了C ++ 11处理,甚至提供了C ++ 03标头保护。
jww 16/09/27

Answers:


68

使用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文件中,然后在需要的地方添加即可。

或者,你可以下载微软的版本stdint.h从文件在这里,或者使用从便携式一个在这里


请参阅stackoverflow.com/questions/126279/…,以获取有关如何获取适用于MSVC(可能还有Borland)的stdint.h的信息。
Michael Burr

2
两个链接都坏了!
安东尼奥

1
这个答案与C有关,但是语言被标记为C ++,所以它不是我要找的答案。
HaseeB Mir

@HaSeeBMiR适当的解决方法是切换到<cstdint>,或下载相应的cstdint,如果你下载stdint.h
贾斯汀时间-恢复莫妮卡

1
@HaSeeBMiR答案与C而不是C ++有关的唯一原因是它使用C头文件而不是等效的C ++头文件。C预处理程序是C ++的一部分,并且cstdint是C ++标准的一部分,那里定义的所有类型名称也是如此。确实适合于指定的标签。...我不同意手动定义类型,但是当使用不这样做的编译器时,可能有必要。
贾斯汀时间-恢复莫妮卡

91

我会说这是现代的C ++方式。

#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);

编辑

正确的整数类型

因此将指针存储为整数的正确方法是使用uintptr_torintptr_t类型。(另请参见cppreference整数类型中的C99)。

这些类型在<stdint.h>C99中定义,在stdC ++ 11中的命名空间中定义<cstdint>(请参阅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是此转换的正确转换(另请参见此处)。

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

相关问题


6
唯一正确提及reinterpret_cast的答案
–plasmcel

如果要包含<cstdint>,则可能还想使用std :: uintptr_t。
linleno

太棒了...演员就是我想要的。如果告诉我们使用uintptr_t代替size_t,那么为什么需要reinterpret_cast呢?似乎很容易static_cast做,因为该标准专门提供了兼容的数据类型...
jww

1
@jww阅读:en.cppreference.com/w/cpp/language/static_cast我在这里的理解是static_cast可能会转换类型,或者如果它是指针,则在类型需要时可以进行指针调整。reinterpret_cast实际上只是在改变基础内存模式的类型(无突变)。澄清static_cast一下:在这里的行为确实相同。
亚历山大·吴

2
应该将其标记为“选择的答案”,因为它提供了如何在C和C ++中进行强制转换的所有详细信息。
HaseeB Mir

42

需要“ size_t”和“ ptrdiff_t”来匹配您的体系结构(无论它是什么)。因此,我认为您应该能够使用“ size_t”,而不是使用“ int”,它在64位系统上应该是64位类型。

本讨论中的unsigned int与size_t进行了更详细的讨论。


34
虽然size_t通常足够大以容纳指针,但不一定如此。最好找到一个stdint.h标头(如果您的编译器尚没有标头)并使用uintptr_t。
Michael Burr

3
不幸的是,唯一的限制size_t是它必须包含any的结果sizeof()。这不一定会使它在x64上变为64位。另请参阅
Antoine

3
size_t 可以安全地存储非成员指针的值。请参阅en.cppreference.com/w/cpp/types/size_t
AndyJost

2
@AndyJost不,它不能。甚至您自己的链接也可以确认这一点。
yyny

1
@YoYoYonnY:“在许多平台上(具有分段寻址的系统除外)std :: size_t可以安全地存储任何非成员指针的值,在这种情况下,它与std :: uintptr_t同义。” -你在说什么?
slashmais


8

有几个答案指向uintptr_t#include <stdint.h>称为“解决方案”。我建议,这是答案的一部分,而不是全部答案。您还需要查看消息ID为FOO的函数的调用位置。

考虑以下代码和编译:

$ 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.hinttypes.h提供的服务stdint.h,这是不寻常的,但是C99标准指出,标题<inttypes.h>包含标题<stdint.h>和通过托管实现提供的其他功能对其进行扩展),并在格式字符串中使用PRIxxx宏。

另外,我的评论严格适用于C而不是C ++,但是您的代码位于C ++的子集中,该子集可在C和C ++之间移植。我的评论适用的机会是公平的。


3
我想你错过了我的问题的重点。该代码将整数值存储在指针中。而那部分代码则相反(例如,提取作为指针写入的整数的值)。
PierreBdR 2014年

@PierreBdR尽管如此,他的观点很正确。查看使用带符号int但用于大小的代码(包括编译器警告它的代码)并不总是那么简单,并且认为可以将其更改为unsigned。不幸的是,这并不总是那么简单。您必须明确地查看每种情况,除非您想引起潜在的错误-并导致细微的错误。
Pryftan

4
  1. #include <stdint.h>
  2. 使用uintptr_t在随附的标准头文件中定义的标准类型。

3

在研究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.
*/

功劳归于提交者。


2

最好的办法是避免从指针类型转换为非指针类型。但是,对于您而言,这显然是不可能的。

就像每个人都说的那样,您应该使用uintptr_t。

链接具有有关转换为64位代码的良好信息。

comp.std.c上也对此进行了很好的讨论


2

我认为在这种情况下,void *的“含义”是通用的句柄。它不是指向值的指针,而是值本身。(这恰好是C和C ++程序员使用void *的方式。)

如果它持有整数值,则最好在整数范围内!

这很容易呈现为整数:

int x = (char*)p - (char*)0;

它应该只发出警告。


0

由于uintptr_t不能保证有在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也不是答案:没有担保,您可以在其中存储指针的值!可能没有整数类型可以做到这一点。
PierreBdR
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.