c - 为什么在一个表达式中同时使用左移和右移会有所不同?

标签 c operators language-lawyer bit-shift integer-promotion

我有以下代码:

unsigned char x = 255;
printf("%x\n", x); // ff

unsigned char tmp = x << 7;
unsigned char y = tmp >> 7;
printf("%x\n", y); // 1

unsigned char z = (x << 7) >> 7;
printf("%x\n", z); // ff

我本来以为 yz是一样的。但它们因是否使用中间变量而异。知道为什么会这样会很有趣。

最佳答案

这个小测试实际上比它看起来更微妙,因为行为是实现定义的:

  • unsigned char x = 255;这里没有歧义,xunsigned char带值 255 , 类型 unsigned char保证有足够的范围来存储 255 .
  • printf("%x\n", x);这会产生 ff在标准输出上,但写 printf("%hhx\n", x); 会更干净如 printf期待 unsigned int用于转换 %x , 其中 x不是。路过x实际上可能会通过 intunsigned int争论。
  • unsigned char tmp = x << 7;计算表达式 x << 7 , x成为 unsigned char首先经历 C 标准中定义的整数提升 6.3.3.1 : 如果是 int可以表示原始类型的所有值(受宽度限制,对于位域),该值转换为 int ;否则,它被转换为 unsigned int .这些被称为整数提升。

    因此,如果 unsigned char 中的值位数小于或等于int (目前最常见的情况是 8 对 31),x首次晋升为int具有相同的值,然后左移 7职位。结果,0x7f80 , 保证适合 int类型,因此行为定义良好并将此值转换为类型 unsigned char将有效地截断值的高位。如果输入 unsigned char有 8 位,值为 128 ( 0x80 ),但如果输入 unsigned char有更多位,tmp 中的值可以 0x180 , 0x380 , 0x780 , 0xf80 , 0x1f80 , 0x3f80甚至 0x7f80 .

    如果输入 unsigned char大于 int ,这可能发生在罕见的系统上,其中 sizeof(int) == 1 , x晋升为 unsigned int并在此类型上执行左移。值为 0x7f80U ,保证适合类型 unsigned int并将其存储到 tmp由于输入 unsigned char,实际上并没有丢失任何信息大小与 unsigned int 相同.所以tmp将具有值 0x7f80在这种情况下。
  • unsigned char y = tmp >> 7;评估过程同上,tmp晋升为 intunsigned int取决于系统,它保留其值,并且该值右移 7 个位置,这是完全定义的,因为 7小于类型的宽度( intunsigned int )并且值为正。取决于 unsigned char 类型的位数,存储在 y 中的值可以 1 , 3 , 7 , 15 , 31 , 63 , 127255 ,最常见的架构会有y == 1 .
  • printf("%x\n", y);再次,最好不要写 printf("%hhx\n", y);并且输出可能是 1 (最常见的情况)或 3 , 7 , f , 1f , 3f , 7fff取决于类型中的值位数 unsigned char .
  • unsigned char z = (x << 7) >> 7;整数提升在 x 上执行如上所述,值 ( 255 ) 然后左移 7 位作为 intunsigned int ,一直在生产0x7f80然后右移 7 个位置,最终值为 0xff .这种行为是完全定义的。
  • printf("%x\n", z);再次,格式字符串应该是 printf("%hhx\n", z);并且输出总是 ff .

  • 如今,字节超过 8 位的系统变得越来越少,但某些嵌入式处理器(例如专用 DSP)仍在这样做。当通过 unsigned char 时,一个反常的系统才会失败。对于 %x转换说明符,但使用 %hhx 更干净或者更便携地写 printf("%x\n", (unsigned)z);
    转移 8而不是 7在这个例子中会更加人为。它在 16 位系统上会有未定义的行为 int和 8 位 char .

    关于c - 为什么在一个表达式中同时使用左移和右移会有所不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61958789/

    相关文章:

    c++ - OpenCV : convert the pointer in memory to image

    c - 如何接受超时套接字

    c++ - 不同类型参数的重载函数?

    c - 对于浮点宏,C 标准的 "converted to its semantic type"是什么意思?

    java - 使用方法引用和原始类型的函数接口(interface)特化的重载解析

    c++ - 该代码标准是否合规?

    c - 树算法中内存的动态分配和重新分配

    c - 如何正确使用可变函数

    c++ - 不需要 std::generate 中的运算符 ()?

    operators - 复制操作符为 0