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

时间:2011-10-06 17:06:33

标签: c types casting type-conversion

我使用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所指出的那样:

  

将签名类型(或任何类型)转换为无符号类型   始终通过减少模数加上最大值来实现   目的地类型。

(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当具有整数类型的值转换为另一个整数时 如果值可以由new表示,则除_Bool以外的类型 类型,它没有变化。
  • 2否则,如果新类型是无符号的,则转换为 重复加或减一个以上的最大值 可以用新类型表示,直到值在 新类型的范围.49)
  • 3否则,新类型已签名且值不可 代表其中;结果是实现定义的还是 提出了实现定义的信号。
  • 49)规则描述了数学值的算术,而不是 给定类型表达式的值。

4 个答案:

答案 0 :(得分:6)

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

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

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

答案 1 :(得分:0)

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

  

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

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

答案 2 :(得分:0)

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

0x00000000FFFFFFFFULL

答案 3 :(得分:0)

使用它来避免符号扩展名:

unsigned __int64 a = 0x00000000FFFFFFFFLL;

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