c -左位移位和强制转换



我有一个我不理解的行为,我试图从字节数组从大端到小端构造一个64整数。

uint64_t u;
uint8_t bytes[2];

bytes[1] = 0xFF;
u =  bytes[1] << 24 ;
dump_bytes_as_hex( &u, 8 );

00 00 00 FF FF FF FF FF

u =  ( (uint16_t) bytes[1]) << 24 ;
dump_bytes_as_hex( &u, 8 );

00 00 00 FF FF FF FF FF

u =  ( (uint32_t) bytes[1]) << 24 ;
dump_bytes_as_hex( &u, 8 );

00 00 00 FF 00 00 00 00 00

我不明白为什么只有当我转换为比移位大小多的类型时才会给我正确的结果。我尝试了不同的值:

  • 0xFF-1给出相同的坏结果
  • 100给出正确结果

所以我想知道规则是什么?为什么100给我正确的值

谢谢。编辑:

下面是一个可复制的例子:

#include <stdio.h>
#include <stdint.h>

void dump_bytes_as_hex( uint8_t* b, int count )
{
FILE* f;
f = stdout;
for( int c = 0; c < count; ++c )
{
fprintf( f, "%02X", b[c] );
fputc( ' ', f );
}
fputc( 'n', f );
fflush( f );
}
void test( uint8_t i )
{
uint64_t u;
uint8_t bytes[2];
fprintf( stdout, "Test with %dn", (int) i );
u = 0;
bytes[1] = i;
u =  bytes[1] << 24 ;
dump_bytes_as_hex( (uint8_t*) &u, 8 );
u =  ( (uint16_t) bytes[1]) << 24 ;
dump_bytes_as_hex( (uint8_t*) &u, 8 );
u =  ( (uint32_t) bytes[1]) << 24 ;
dump_bytes_as_hex( (uint8_t*) &u, 8 );
fprintf( stdout, "nn");
}

int main()
{
test( 0xFF );
test( 0xFF -1  );
test( 100 );
return 0;
}

所以我想知道规则是什么?

事实上,你的具体例子没有规则…

bytes[1] = 0xFF;
u =  bytes[1] << 24 ;

…和…

u =  ( (uint16_t) bytes[1]) << 24 ;
dump_bytes_as_hex( &u, 8 );

…如果你的int是32位宽,

移位操作的左操作数服从通常的算术转换,它将左操作数的8位或16位无符号值转换为(signed)int并产生该类型的结果。如果有符号类型()的值左移的算术结果int)不能表示为该类型的值,那么行为是未定义的。这就是这里的情况(这就是为什么我说没有规则)。

在您的特殊情况下,似乎您的实现正在执行移位,好像通过将左操作数重新解释为无符号32位值,将结果重新解释为(有符号)int。然后对类型uint64_t赋值(通常)通过将右负操作数加上264使其进入该类型的范围,将其转换为tyuint64_t来继续。(此类转换仅在转换为无符号整数类型时执行,而不是转换为有符号整数类型时执行。)您正在一个小端系统上工作,并按内存顺序打印结果字节,因此您得到:

00 00 00 FF FF FF FF FF

另一方面,如果在32-0位系统上将移位操作数转换为uint32_t,则…

u =  ( (uint32_t) bytes[1]) << 24 ;
dump_bytes_as_hex( &u, 8 );

…然后,生成的类型不受标准算术转换的影响,并且转换的结果具有该类型。uint32_t可以表示移位的算术结果(0xff000000),因此这实际上是结果。当转换为类型uint64_t时,该值不变。


一般建议:在处理位操作时使用unsigned类型,特别是在执行移位操作时。

给结果赋值的变量的类型(如果有的话)是无关的。

只有当左操作数的类型是经过整型提升后可以保存结果的类型时才有效。

  • (uint8_t)0xFF << 24

    int类型为16的环境中…32位,(uint8_t)0xFF提升为int。0xFF x224太大,无法在int中保存。作为有符号类型,这将导致未定义行为。

    int类型为33+位的环境下,(uint8_t)0xFF被提升为int。0xFF × 224适合int。这工作。

  • (uint16_t)0xFF << 24

    int类型为16的环境中…24位,(uint16_t)0xFF得到16位的unsigned int。左移大于或等于操作数的位数是未定义的行为。

    int类型为25的环境中…32位,(uint16_t)0xFF得到int。0xFF x224太大,无法在int中保存。作为有符号类型,这将导致未定义行为。

    int类型为33+位的环境中,(uint16_t)0xFF的结果是int。0xFF × 224适合int。这工作。

  • (uint32_t)0xFF << 24

    0xFF x224适合32位无符号整数。这工作。

因此,要生成uint64_t,您需要

(uint64_t)( (uint64_t)u8 << 24 )

(uint64_t)( (uint32_t)u8 << 24 )

但是,由于赋值操作将隐式地执行外强制转换,因此可以在此处删除外强制转换。

相关内容

  • 没有找到相关文章

最新更新