100

我正在尝试使现有代码适应 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 位机器上运行,并且尽可能干净。任何的想法 ?

4

11 回答 11

115

我会说这是现代 C++ 方式:

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

编辑

Integer 的正确类型

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

这些类型在<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

相关问题

于 2014-10-27T11:07:40.543 回答
73

使用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 文件中并包含在您需要的任何地方。

或者,您可以从此处下载 Microsoft 版本的stdint.h文件,或从此处使用便携式文件。

于 2008-09-30T13:45:34.987 回答
43

'size_t' 和 'ptrdiff_t' 需要与您的架构相匹配(无论它是什么)。因此,我认为与其使用“int”,不如使用“size_t”,在 64 位系统上应该是 64 位类型。

这个讨论unsigned int vs size_t更详细一点。

于 2008-09-30T15:05:50.503 回答
16

用作uintptr_t您的整数类型。

于 2008-09-30T13:46:37.697 回答
8

有几个答案指出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
$

您将观察到调用位置 (in 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 宏。

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

于 2008-09-30T14:20:32.467 回答
4
  1. #include <stdint.h>
  2. 使用uintptr_t包含的标准头文件中定义的标准类型。
于 2010-11-10T11:23:50.250 回答
4

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

归功于提交者。

于 2017-03-25T17:11:55.120 回答
2

最好的办法是避免从指针类型转换为非指针类型。但是,在您的情况下,这显然是不可能的。

正如大家所说, uintptr_t 是你应该使用的。

这个链接有关于转换为 64 位代码的好信息。

在comp.std.c上也有很好的讨论

于 2008-09-30T14:05:07.247 回答
2

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

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

这是整数的简单渲染:

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

它应该只给出警告。

于 2011-11-10T06:21:15.280 回答
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");
于 2018-03-16T23:04:24.503 回答
0

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

于 2021-11-24T07:24:38.503 回答