我正在尝试将现有代码调整为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位机器上运行并且尽可能干净。有什么想法吗?
答案 0 :(得分:70)
我会说这是现代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
中为{+ 1}}中的C ++ 11定义(参见integer types for C++)。
C ++ 11(及以后)版本
<cstdint>
C ++ 03版
#include <cstdint>
std::uintptr_t i;
C99版本
extern "C" {
#include <stdint.h>
}
uintptr_t i;
在C中只有一个强制转换,并且在C ++中使用C强制转换是不受欢迎的(所以不要在C ++中使用它)。在C ++中有不同的演员阵容。 #include <stdint.h>
uintptr_t i;
是此转换的正确广播(另请参阅here)。
C ++ 11版本
reinterpret_cast
C ++ 03版
auto i = reinterpret_cast<std::uintptr_t>(p);
C版
uintptr_t i = reinterpret_cast<uintptr_t>(p);
答案 1 :(得分:66)
使用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文件中,并将其包含在您需要的任何位置。
答案 2 :(得分:41)
此讨论unsigned int vs size_t详细介绍了一些内容。
答案 3 :(得分:16)
使用uintptr_t
作为整数类型。
答案 4 :(得分:7)
有几个答案指出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.h
- inttypes.h
提供stdint.h
的服务,这是不寻常的,但C99标准说 [t]标题<inttypes.h>
包含标题<stdint.h>
并将其扩展为
托管实现提供的其他工具)并在格式字符串中使用PRIxxx宏。
此外,我的注释严格适用于C而不是C ++,但您的代码位于C ++的子集中,可以在C和C ++之间移植。我的评论适用的机会是公平的。
答案 5 :(得分:4)
#include <stdint.h>
uintptr_t
标准类型。答案 6 :(得分:4)
我认为在这种情况下void *的“含义”是一个通用句柄。 它不是指向值的指针,而是值本身。 (这恰好是C和C ++程序员使用void *的方式。)
如果它持有一个整数值,最好在整数范围内!
这里很容易渲染为整数:
int x = (char*)p - (char*)0;
它应该只发出警告。
答案 7 :(得分:2)
我在研究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 :(得分:1)
最好的办法是避免从指针类型转换为非指针类型。 但是,在您的情况下,这显然是不可能的。
正如大家所说,你应该使用uintptr_t。
这个link有关于转换为64位代码的良好信息。
上也有一个很好的讨论答案 9 :(得分:0)
由于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");