下面的行(纯c)在windows(win7 64位+代码块13 + mingw32)和debian(wheezy 32位+代码块10 + gcc)上干净地编译,但在kali(64位+代码块+ gcc)上引发警告.任何意见?我的意思是,为什么我会收到这个警告,虽然同一行编译没有窗口和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... }
codeblocks中的消息是:" error:从指针转换为不同大小的整数[-Werror = pointer-to-int-cast]"
注意:我的编译器选项是-std=c99 -Werror -save-temps
(在所有三个系统上都相同).
编辑2:虽然我已经设法使用下面的预处理器行编译无警告,但@Keith Thompson(见下文)对此问题有一个关键点.所以,我最后的决定是使用uintptr_t
将是一个更好的选择.
编辑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
然后将问题行替换为:
unsigned int offset = (MAX_BLOCK_SIZE) dst % blkLen;
现在,一切似乎都好.
问题是将void*
指针转换unsigned int
为本质上是不可移植的.
尺寸的可能差异只是问题的一部分.问题的这一部分可以通过使用uintptr_t
,<stdint.h>
和中定义的类型来解决<inttypes.h>
.uintptr_t
保证足够宽,以便将a转换void*
为uintptr_t
和返回将产生原始指针值(或至少比较原始指针值的指针值).还有一种intptr_t
签名类型; 通常无符号类型对于这种事情更有意义.uintptr_t
并且intptr_t
不保证存在,但它们应该存在于具有适当整数类型的任何(C99或更高版本)实现上.
但即使你有一个大到足以保存转换指针的整数类型,结果对于转换回指针以外的任何其他内容都不一定有意义.
C标准在非规范性脚注中说:
用于将指针转换为整数或整数到指针的映射函数旨在与执行环境的寻址结构一致.
除非您碰巧知道该寻址结构是什么,否则这没有用.
您似乎试图确定void*
参数的偏移量相对于下一个较低的倍数blkLen
; 换句话说,您正在尝试确定指针值如何与大小的内存块对齐blkLen
.
如果你碰巧知道在你正在使用的系统上这是一个明智的做法,那很好.但是你应该知道,对指针转换产生的整数的算术运算本身仍然是不可移植的.
一个具体的例子:我在系统(Cray向量机)上工作,其中void*
指针是64位机器地址(指向64位字),软件将3位字节偏移插入到未使用的高阶 3位.将指针转换为整数只是复制了表示.对这样的整数进行的任何整数运算都可能产生无意义的结果,除非它考虑到这种(无可否认的奇特)表示.
结论:
你绝对应该使用uintptr_t
而不是使用预处理器技巧来确定你可以使用哪种整数类型.编译器的实现者已经完成了确定可以安全地保存转换指针值的整数类型的工作.没有必要重新发明那个特定的轮子.(警告:<stdint.h>
已经被1999 ISO标准添加到C中.如果你使用的是一个没有实现它的古老编译器,你可能仍然需要使用某种#ifdef
黑客.但我还是建议使用uintptr_t
它是否可用您可以测试__STDC_VERSION__ >= 199901L
以测试C99一致性 - 尽管某些编译器可能会支持<stdint.h>
而不完全支持C99.)
您需要注意,将指针转换为整数并使用其值进行播放是不可移植的.这并不是说你不应该这样做; C的最大优势之一是它支持非可移植代码的能力,而这正是您所需要的.
警告的原因是编译器怀疑您可能正在尝试往返指针往返int
.这是64位计算机出现之前的常见做法,并且不安全或不合理.当然,编译器在这里可以清楚地看到你没有这样做,如果它足够智能以避免在这种情况下发出警告,那就太好了,但事实并非如此.
避免警告的一个干净的替代方案,以及当转换后的值为负时另一个更糟糕的错误结果问题是:
unsigned int offset = (uintptr_t) dst % blkLen;
你需要包括stdint.h
或inttypes.h
有uintptr_t
可用.