转换为uint64时,int32或32位指针的意外符号扩展

我使用Visual Studio 2010( cl.exe /W4 )将此代码编译为C文件:

 int main( int argc, char *argv[] ) { unsigned __int64 a = 0x00000000FFFFFFFF; void *orig = (void *)0xFFFFFFFF; unsigned __int64 b = (unsigned __int64)orig; if( a != b ) printf( " problem\ta: %016I64X\tb: %016I64X\n", a, b ); return; } 

没有警告,结果是:

问题a:00000000FFFFFFFF b:FFFFFFFFFFFFFFFF

我想int orig = (int)0xFFFFFFFF会引起争议,因为我没有指定一个整数的指针。 但结果是一样的。

有人可以向我解释在C标准中它覆盖了orig是从0xFFFFFFFF扩展到0xFFFFFFFFFFFFFFFF的符号吗?

我原以为(unsigned __int64)orig会变成0x00000000FFFFFFFF。 似乎转换首先是签名的__int64类型,然后它变为无符号?

编辑:这个问题已被回答,指针是符号扩展,这就是为什么我在gcc和msvc中看到这种行为。 但是我不明白为什么当我执行类似(unsigned __int64)(int)0xF0000000它的符号扩展到0xFFFFFFFFF0000000但是(unsigned __int64)0xF0000000并没有反而显示我想要的是0x00000000F0000000。

编辑:上述编辑的答案。 (unsigned __int64)(int)0xF0000000符号扩展的原因是因为,如用户R所述 :

有符号类型(或任何类型)到无符号类型的转换总是通过减少模1加上目标类型的最大值来进行。

并且在(unsigned __int64)0xF0000000 0xF0000000作为无符号整数类型开始,因为它不能适合整数类型。 接下来,已经无符号的类型转换为unsigned __int64

因此,对我来说这是一个函数,它返回一个32位或64位指针作为unsigned __int64进行比较我必须首先将32位应用程序中的32位指针转换为无符号类型,然后再将其提升为unsigned __int64 。 结果代码看起来像这样(但是,你知道,更好):

 unsigned __int64 functionidontcontrol( char * ); unsigned __int64 x; void *y = thisisa32bitaddress; x = functionidontcontrol(str); if( x != (uintptr_t)y ) 

再次编辑:这是我在C99标准中找到的:6.3.1.3有符号和无符号整数

  • 1当具有整数类型的值转换为除_Bool之外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变。
  • 2否则,如果新类型是无符号的,则通过重复加或减一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内.49)
  • 3否则,新类型已签名且值无法在其中表示; 结果是实现定义的,或者引发实现定义的信号。
  • 49)规则描述了数学值的算术,而不是给定类型表达式的值。

将指针转换为/从整数转换是实现定义的。

下面是gcc如何做到这一点,即如果整数类型大于指针类型,则符号会扩展(无论整数是有符号还是无符号,都会发生这种情况,只是因为这是gcc决定实现它的方式)。

据推测,msvc表现相似。 编辑,我在MSDN上找到的最接近的东西就是这个 / 这个 ,这表明将32位指针转换为64位也符号扩展。

根据C99标准(§6.3.2.3/ 6):

任何指针类型都可以转换为整数类型。 除了之前指定的以外, 结果是实现定义的 。 如果结果无法以整数类型表示,则行为未定义。 结果不必在任何整数类型的值范围内。

所以你需要找到你的编译器文档来讨论它。

整数常量(例如, 0x00000000FFFFFFFF )默认为有符号整数,因此在分配给64位变量时可能会出现符号扩展。 尝试将第3行的值替换为:

 0x00000000FFFFFFFFULL 

使用它来避免符号扩展:

 unsigned __int64 a = 0x00000000FFFFFFFFLL; 

注意最后的L. 如果没有它,它将被解释为32位有符号数(-1),然后进行转换。