C语言 左位移位和铸造

0md85ypi  于 2023-04-29  发布在  其他
关注(0)|答案(2)|浏览(178)

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

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

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

00 00 00 FF FF FF FF

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

00 00 00 00 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 %d\n", (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, "\n\n");
}


int main()
{

    test( 0xFF );
    test( 0xFF -1  );
    test( 100 );

    return 0;

}
hyrbngr7

hyrbngr71#

我想知道规则是什么?
事实上,对于你的具体例子,没有任何规则。..

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

...还有...

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

...如果您的int是32位宽。
移位操作的左操作数是 * 通常的算术转换 *,这将具有将左操作数的8位或16位无符号值转换为(signedint并产生该类型的结果的效果。如果有符号类型(*i.即 * int)不能表示为该类型的值,则行为未定义。这就是为什么我说没有规则的原因。
在您的特定情况下,您的实现似乎是通过将左操作数重新解释为无符号32位值来执行移位,将结果重新解释为(有符号)int。然后,对类型uint64_t的赋值(通常)通过添加264将负右操作数转换为ty uint64_t,以将其带入该类型的范围。(此类转换仅用于转换为无符号整数类型,而不用于转换为有符号整数类型。你正在使用一个little-endian系统,并按内存顺序打印出结果字节,所以你得到:
00 00 00 FF FF FF FF
另一方面,如果在32位系统上将移位操作数转换为uint32_t。..

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

...则结果类型不受标准算术转换的影响,并且转换的结果具有该类型。uint32_t可以表示移位(0xff000000)的算术结果,因此这实际上是结果。当转换为uint64_t类型进行赋值时,该值不变。
一般建议:在使用按位操作时使用无符号类型,尤其是在执行移位时。

k3bvogb1

k3bvogb12#

将结果赋给的变量的类型(如果有的话)是无关的。
只有当左操作数的类型是整数提升后可以保存结果的类型时,它才有效。

  • (uint8_t)0xFF << 24

int类型为16的环境中。.32位,则(uint8_t)0xFF被提升为int。0xFF × 224太大,无法容纳在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 × 224太大,无法容纳在int中。作为有符号类型,这会导致未定义的行为。
int类型为33+位的环境中,(uint16_t)0xFF将产生int。0xFF × 224适合int。这个管用

  • (uint32_t)0xFF << 24

0xFF × 224适合32位无符号整数。这个管用
因此,要生成uint64_t,您需要

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

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

但是,外部强制转换可以在这里删除,因为赋值将隐式执行此强制转换。

相关问题