在执行“〈〈”操作时自动用f填充c中的位

2wnc66cl  于 2023-03-17  发布在  其他
关注(0)|答案(3)|浏览(149)

我在玩C中的位操作,我不明白一件事,如果我有char b = 0x6c,然后我有short y = 0x0000,然后我做y=b; y = y << 12;,为什么它用'f'填充我的y
(编者注:>><<的方向很重要)

#include <stdio.h>
#include <stdlib.h>

int main()
{
    short x = 0x0000;
    short y = 0x0000;
    char a = 0x00;
    char b = 0x00;
    char c = 0x00;

    a = 'a'; /// 61
    b = 'l'; /// 6c
    c = 'b'; /// 62

    x = a;
    x = x<<8;
    y = b;
    x = x + b;
    x = x >> 4;
    x = x << 4;

    y = y << 12;
    printf(" x + temp = %#06x y = %#06x\n", x, y);
return 0;
}
kdfy810k

kdfy810k1#

在具有16位int的系统上,y << 12导致未定义的行为,因为y具有带符号类型,并且0x 6C 000太大而无法放入int
在具有32位(或更大)int和16位short的系统上,y = y << 12结果由实现定义,或引发实现定义的信号。
第二段可能适用于你。
同样,%x需要unsigned int,而不是short,因此printf也会导致未定义的行为。
因此,程序表现出实现定义和/或未定义的行为。
固定:

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

int main( void ) {
   char a = 'a';  // 0x61
   char b = 'l';  // 0x6C
   char c = 'b';  // 0x62

   uint16_t x
      = ( ( (uint16_t)a & 0x00FF ) <<  8 )
      | ( ( (uint16_t)b & 0x00F0 ) <<  0 );

   uint16_t y
      = ( ( (uint16_t)b & 0x000F ) << 12 )
      | ( ( (uint16_t)c & 0x00FF ) <<  4 );

   // x = 6160 y = C620
   printf(" x = %04" PRIX16 " y = %04" PRIX16 "\n", x, y );

   return 0;
}

Demo(位于编译器资源管理器上)。

bmvo0sr5

bmvo0sr52#

作为对前面优秀的answer的补充,我将使用一个不调用未定义行为的示例来解释发生了什么,该示例具有相同的行为。

int main()
{
    unsigned short x = 0x6c;
    short y;

    x <<= 12;
    y = x;

    printf("x = %u y = %d\n", x, y);
    printf("x = 0x%x y = 0x%x\n", (unsigned)x, (unsigned)y);
}

结果:

x = 49152 y = -16384
x = 0xc000 y = 0xffffc000
  1. x具有值0x6c,当左移12位时,其变为0xc000
    1.分配给有符号短整型(二进制补码系统)的0xc000-16384
  2. -16384的二进制补码32位整数是0xffffc000,这就是您看到此数字的原因
7y4bm7vi

7y4bm7vi3#

如果我有字符B = 0x 6c,然后我有短的y = 0x 0000,我做y=b;y = y〉〉12;,为什么我的y里都是“f”?
假设...

char b = 0x6c;
short y = 0x0000;
y=b;

...然后存储在y中的值是0x6c。需要类型short才能表示该值。

  • 假设类型int可以表示它 *,那么y << 12的结果就被很好地定义为 *an int *,值为0x6c000。C int s可以窄到16位,这是不够的,但是现在大多数实现提供32位int s,这是足够的。

然而,很少有实现提供超过16位的short,因此y不太可能表示该结果。

y = y << 12;

...具有实现定义的结果或导致引发实现定义的信号。这不是 un 定义的行为。注意2:对于unsigned short或任何其它无符号类型,要求将是不同的。
这种赋值的典型行为是存储最低有效的16位,而其他位丢失,这将给予存储在y中的位模式0xc000,作为16位二进制补码short的值,该位模式表示负数(十进制-16384)。
当你试图打印结果时,short值被隐式转换为相同值的int表示。你的int显然是32位宽,并且负二进制补码表示的值保持加宽需要用1而不是零填充额外的前导位。因此,传入的第三个参数...

printf(" x + temp = %#06x y = %#06x\n", x, y);

...是具有以下位模式的int0xffffc000 .

现在我们遇到了一些未定义的行为。在printf中,与%x转换说明符匹配的参数值必须是unsigned int,但实际传递的值是(signedintprintf参数之间的这种不匹配会产生未定义的行为。但是,在这种特定类型的int/unsigned int不匹配中,未定义行为的通常表现形式是int位模式被解释为unsigned int位模式。您报告的输出与这种表现形式一致。

那么你应该怎么做呢?首先,在进行移位和位操作时,你通常应该使用无符号数据类型。其次,你应该注意(最小)宽度,并确保使用的类型宽度足以满足您的目的。如果您#include <stdint.h>,则(很可能)将能够使用类型uint16_t,这是一个无符号整数数据类型,正好有16位,全部是值位。但是,这确实使您的printf调用复杂化。或者,您可以使用unsigned short,一个至少有16个值位的无符号整数类型,并且屏蔽掉除最低有效16位之外的所有位,或者甚至假设您的unsigned short正好有16个值位--这一点得到了广泛的应用,尽管不是普遍的。

相关问题