C语言 为什么会出现“从指针转换为不同大小的整数”错误?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26805461/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why do I get "cast from pointer to integer of different size" error?
提问by ssd
The following line (pure c) compiles cleanly on windows(win7 64 bits + codeblocks 13 + mingw32) and debian(wheezy 32 bits + codeblocks 10 + gcc) but raises warning on kali(64 bits + codeblocks + gcc). Any comments? I mean, why do I get this warning, though the same line compiles w/o any warning on windows & debian?
以下行(纯 c)在windows(win7 64 位 + 代码块 13 + mingw32)和debian(wheezy 32 位 + 代码块 10 + gcc)上编译干净,但在kali(64 位 + 代码块 + gcc)上引发警告。任何意见?我的意思是,尽管同一行在 Windows 和 debian 上编译时没有任何警告,但为什么我会收到此警告?
void* foo(void *dst, ...) {
// some code
unsigned int blkLen = sizeof(int); // this line ok.
unsigned int offset = (unsigned int) dst % blkLen; // warning here!
// some code cont...
}
The message in codeblocks is: "error:cast from pointer to integer of different size [-Werror=pointer-to-int-cast]"
代码块中的消息是:“错误:从指针转换为不同大小的整数 [-Werror=pointer-to-int-cast]”
note: my compiler options are -std=c99 -Werror -save-temps(same on all three systems).
注意:我的编译器选项是-std=c99 -Werror -save-temps(在所有三个系统上都相同)。
edit 2:
Though I've managed to have it compiled w/o warning using the preprocessor lines below,
@Keith Thompson (see below) has a crucial point about the issue. So, my last decision is using uintptr_twould be a better choice.
编辑 2:虽然我已经设法使用下面的预处理器行在没有警告的情况下编译它,@Keith Thompson(见下文)对这个问题有一个关键点。所以,我最后的决定是使用uintptr_t将是一个更好的选择。
edit 1: Thanks for everyone replied. As all the replies note, the problem is a 32 bits vs 64 bits issue. I've inserted following preprocessor lines:
编辑1:感谢大家的回复。正如所有回复所指出的,问题是 32 位与 64 位的问题。我插入了以下预处理器行:
#if __linux__ // or #if __GNUC__
#if __x86_64__ || __ppc64__
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#else
#if _WIN32
#define ENVIRONMENT32
#else
#define ENVIRONMENT64
#endif
#endif // __linux__
#ifdef ENVIRONMENT64
#define MAX_BLOCK_SIZE unsigned long long int
#else
#define MAX_BLOCK_SIZE unsigned long int
#endif // ENVIRONMENT64
and then replaced the problem line as:
然后将问题行替换为:
unsigned int offset = (MAX_BLOCK_SIZE) dst % blkLen;
Now, everything seems OK.
现在,一切似乎都很好。
回答by R.. GitHub STOP HELPING ICE
The reason for the warning is that the compiler suspects you might be trying to round-trip a pointer through intand back. This was common practice before the advent of 64-bit machines and it is not safe or reasonable. Of course here the compiler can clearly see that you're not doing this, and it would be nice if it were smart enough to avoid the warning in cases like this, but it's not.
警告的原因是编译器怀疑您可能试图来回往返指针int。这是 64 位机器出现之前的常见做法,它不安全或不合理。当然,这里编译器可以清楚地看到您没有这样做,如果它足够聪明以避免在这种情况下发出警告会很好,但事实并非如此。
A clean alternative that avoids the warning, and another much nastier issue of wrong result when the converted value is negative, is:
一个避免警告的干净替代方案,以及当转换值为负时另一个更严重的错误结果问题,是:
unsigned int offset = (uintptr_t) dst % blkLen;
You'll need to include stdint.hor inttypes.hto have uintptr_tavailable.
你需要包括stdint.h或inttypes.h有uintptr_t可用。
回答by Keith Thompson
The problem is that converting a void*pointer to unsigned intis inherently non-portable.
问题是将void*指针转换unsigned int为本质上是不可移植的。
The possible difference in size is only part of the problem. That part of the problem can be solved by using uintptr_t, a type defined in <stdint.h>and <inttypes.h>. uintptr_tis guaranteed to be wide enough that converting a void*to uintptr_tand back again will yield the original pointer value (or at least a pointer value that compares equal to the original one). There's also a type intptr_t, which is signed; usually unsigned types make more sense for this kind of thing. uintptr_tand intptr_tare not guaranteed to exist, but they should exist on any (C99 or later) implementation that has the appropriate integer types.
可能的尺寸差异只是问题的一部分。这部分问题可以通过使用和 中uintptr_t定义的类型来解决。保证足够宽,以便再次转换为和返回将产生原始指针值(或至少与原始指针值相比较的指针值)。还有一个 type ,它是有符号的;通常无符号类型对这种事情更有意义。并且不保证存在,但它们应该存在于具有适当整数类型的任何(C99 或更高版本)实现中。<stdint.h><inttypes.h>uintptr_tvoid*uintptr_tintptr_tuintptr_tintptr_t
But even if you have an integer type that's big enough to hold a converted pointer, the result isn't necessarily meaningful for anything other than converting back to a pointer.
但是,即使您有一个足够大的整数类型来保存转换后的指针,结果对于除了转换回指针之外的任何事情也不一定有意义。
The C standard says, in a non-normative footnote, that:
C 标准在非规范性脚注中说:
The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment.
将指针转换为整数或将整数转换为指针的映射函数旨在与执行环境的寻址结构保持一致。
which is not helpful unless you happen to know what that addressing structure is.
除非您碰巧知道该寻址结构是什么,否则这没有帮助。
You seem to be trying to determine what the offset of the void*argument is relative to the next lower multiple of blkLen; in other words, you're trying to determine how the pointer value is alignedwith respect to blkLen-sized blocks of memory.
您似乎试图确定void*参数的偏移量相对于 的下一个较低倍数blkLen;换句话说,您正在尝试确定指针值如何相对于大小的内存块对齐blkLen。
If you happen to know that that's a sensible thing to do on the system you're using, that's fine. But you should be aware that arithmetic operations on integers resulting from pointer conversions are still inherently non-portable.
如果您碰巧知道在您使用的系统上这样做是明智的,那很好。但是您应该知道,由指针转换产生的整数算术运算本质上仍然是不可移植的。
A concrete example: I've worked on systems (Cray vector machines) where a void*pointer is a 64-bit machine address (which points to a 64-bit word), with a 3-bit byte offset inserted by software into the otherwise unused high-order3 bits. Converting a pointer to an integer simply copied the representation. Any integer arithmetic on such an integer is likely to yield meaningless results unless it takes this (admittedly exotic) representation into account.
一个具体的例子:我在系统(Cray 向量机)上工作过,其中void*指针是 64 位机器地址(指向 64 位字),软件将 3 位字节偏移量插入到其他未使用的位置高位3 位。将指针转换为整数只是简单地复制了表示。除非考虑到这种(公认的奇特)表示,否则对此类整数的任何整数算术都可能产生无意义的结果。
Conclusions:
结论:
You should definitely use
uintptr_trather than playing preprocessor tricks to determine which integer type you can use. The implementer of your compiler has already done the work of determining an integer type that can safely hold a converted pointer value. There's no need to reinvent that particular wheel. (Caveat:<stdint.h>was added to C by the 1999 ISO standard. If you're stuck using an ancient compiler that doesn't implement it, you might still need to use some kind of#ifdefhacks. But I'd still suggest usinguintptr_tif it's available. You can test__STDC_VERSION__ >= 199901Lto test for C99 conformance -- though some compilers might support<stdint.h>without fully supporting C99.)You need to be aware that converting a pointer to an integer and playing with its value is non-portable. That's not to say you shouldn't do it; one of C's greatest strengths is its ability to support non-portablecode when that's what you need.
您绝对应该使用
uintptr_t而不是玩预处理器技巧来确定您可以使用哪种整数类型。编译器的实现者已经完成了确定可以安全保存转换后的指针值的整数类型的工作。没有必要重新发明那个特定的轮子。(警告:<stdint.h>由 1999 年 ISO 标准添加到 C 中。如果您坚持使用没有实现它的古老编译器,您可能仍然需要使用某种#ifdef技巧。但uintptr_t如果它可用,我仍然建议使用。您可以测试__STDC_VERSION__ >= 199901L以测试 C99 一致性——尽管某些编译器可能支持<stdint.h>但不完全支持 C99。)您需要注意将指针转换为整数并使用其值是不可移植的。这并不是说你不应该这样做;C 的最大优势之一是它能够在您需要时支持非可移植代码。
回答by b4hand
Because casting a void *to an unsigned intis exactly what that warning is intended to catch because it is unsafe. The pointer can be 64-bit and the intcan be 32-bit. For any given platform, the sizeof(unsigned int)is not guaranteed to be sizeof(void *). You should use uintptr_tinstead.
因为将 a 转换void *为 anunsigned int正是该警告旨在捕获的内容,因为它不安全。指针可以是 64 位的,int也可以是 32 位的。对于任何给定的平台,sizeof(unsigned int)不保证是sizeof(void *). 你应该uintptr_t改用。
回答by Serge Ballesta
Maybe because on a 64 bits architecture a pointer is 64 bits long and an int in only 32 bits long ?
也许是因为在 64 位体系结构上,指针长度为 64 位,而 int 长度仅为 32 位?
You should try
你应该试试
void* foo(void *dst, ...) {
// some code
unsigned int blkLen = sizeof(int); // this line ok.
uintptr_t offset = (uintptr_t) dst % blkLen; // warning here!
// some code cont...
}
回答by viggy22
I think you get the warning because the size of int depends on the implemetations eg int might be 2 byte long or 4 byte long. This might be the reason for the warning(Please correct me if I am wrong). But anyways why are you trying to do a modulo on a pointer.
我认为您收到警告是因为 int 的大小取决于实现,例如 int 可能是 2 字节长或 4 字节长。这可能是警告的原因(如果我错了,请纠正我)。但无论如何,您为什么要尝试对指针进行模运算。
回答by newbie
Your have make the macro but don't you think its still wrong. Because your pointer will be converted to unsigned long long int or unsigned long int which will be 32 bit and 64 bit in 86x and 64x OS but your variable offset is unsigned int which is 32bit in 64x and 86x OS. So i think you should convert offset also to the respective macro.
您已经制作了宏,但您不认为它仍然是错误的。因为您的指针将被转换为 unsigned long long int 或 unsigned long int,它们在 86x 和 64x 操作系统中为 32 位和 64 位,但您的变量偏移量是 unsigned int,在 64x 和 86x 操作系统中为 32 位。所以我认为您也应该将偏移量转换为相应的宏。
Or simply you can convert the pointer to long (i.e. unsigned int to long) and offset to long (i.e. unsigned int to long) also.
或者,您也可以将指针转换为 long(即 unsigned int 到 long)并将偏移量转换为 long(即 unsigned int 到 long)。

