【问题标题】:Compiler dependency in right shift operator on signed types:右移运算符对有符号类型的编译器依赖性:
【发布时间】:2023-04-06 20:31:01
【问题描述】:

在下面的代码中,

#include <stdio.h>
#include<conio.h>
main()
{
    int y=-01;
    y<<=3;
    printf("%d\n",y);
    y>>=3;
    printf("%d",y);
    getch();
}

我在一本书中读到,在 y 上使用右移运算符时,有符号位可能会或可能不会保留,具体取决于我的编译器。这是为什么?这背后的概念是什么?请详细说明。

【问题讨论】:

    标签: c bit-manipulation signed octal


    【解决方案1】:

    某些处理器只有“无符号”右移(用 0 填充符号位)或有符号右移(用当前符号位的副本填充大小位)。由于某些处理器只有一个或另一个,而不是两者,因此该标准不会尝试强制一种行为或另一种行为。

    对于它的价值,许多(大多数?)当前的处理器都有指令来做这两个。例如,x86 包括shr(逻辑右移——用 0 填充)和sar(算术右移——用符号位的副本填充)。

    【讨论】:

      【解决方案2】:

      以下来自 C11 标准(6.5.7 移位运算符)的引用可能会有所帮助:

      • E1的结果是E1左移E2位;空出的位用零填充。如果 E1 具有无符号类型,则结果的值是 E1 × 2^E2,比结果类型中可表示的最大值减少模一。如果 E1 有带符号的类型和非负值,并且 E1 × 2^E2 在结果类型中是可表示的,那么这就是结果值;否则,行为未定义。
      • E1 >> E2 的结果是 E1 右移 E2 位位置。如果 E1 具有无符号类型或 E1 具有带符号类型和非负值,则结果的值是 E1 / 2^E2 的商的整数部分。 如果 E1 具有带符号类型和负值,则结果值是实现定义的。

      【讨论】:

        【解决方案3】:

        这是真的。因为,有四种类型的变速可用。

        1. Arithmetic Shift

        2. Logical Shift

        3. Rotate No carry Shift

        4. Rotate Through Carry Shift

        在前两种类型中,sign-bit 的位置在向右或向左移动时被保留。通常在 C 或 C 的任何派生中使用Arithmetic ShiftLogical Shift。在 C 中,Right ShiftArithmetic ShiftLeft Shift 是逻辑的。但在 Java 中,两个转换都是Logical Shifting

        在最后两种类型中,Sign Bit 的定位没有被维护。这些位以规则的方式移动。 Rotate No Carry 属于 Circular Shift,主要用于密码学以保留所有位。

        C编程,虽然实现了Logical &amp; arithmetic Shift,但它也有能力通过以下方式识别Circular Shifting

        unsigned int x;
        unsigned int y;
        /* This Code is Just for reference from Wikipedia */    
        y = (x << shift) | (x >> (sizeof(x)*CHAR_BIT - shift));
        

        我这个信息还不够,那你可以看一下Wikipedia主文,上面所有班次的标题都有链接。

        【讨论】:

          【解决方案4】:

          让我们看看这个例子

          考虑这个值1111 1111 1111 1111

          如果你的编译器是 16 位的,你做了右移

          值将变为1111 1111 1111 1110

          如果你的编译器是 32 位的并且你做了右移

          值将变为0000 0000 0000 0001 1111 1111 1111 1110

          你做了相同值的右移,但你会得到不同的结果,具体取决于你的编译器。

          使用 %x 打印十进制值的十六进制表示法。

          查看此代码:

          #include <stdio.h>
          main()
          {
             int y=-01;
              printf("%d\n",y);
              printf("%x\n",y);
          
              y<<=3;
              printf("%d\n",y);
              printf("%x\n",y);
              y>>=3;
              printf("%d\n",y);
              printf("%x\n",y);
          
          
              y=127;
              printf("%d\n",y);
              printf("%x\n",y);
          
              y<<=3;
              printf("%d\n",y);
              printf("%x\n",y);
              y>>=3;
              printf("%d\n",y);
              printf("%x\n",y);
          
          
              y=32767;
              printf("%d\n",y);
              printf("%x\n",y);
          
              y<<=3;
              printf("%d\n",y);
              printf("%x\n",y);
              y>>=3;
              printf("%d\n",y);
              printf("%x\n",y);
          
          
              getchar();
          }
          

          在 64 位机器上输出:

          -1 噗噗噗 -8 ffffff8 -1 噗噗噗 127 7f 1016 3f8 127 7f 32767 7fff 262136 3fff8 32767 7ff

          在 32 位机器上输出:

          -1 噗噗噗 -8 ffffff8 -1 噗噗噗 127 7f 1016 3f8 127 7f 32767 7fff -8 ffffff8 -1 咳咳咳

          当达到 32 位有符号整数的最大值时,您会发现编译器对移位的依赖性。

          这里用 1 和 2 的补码表示来解释 8 位有符号整数
          Signed number representations

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-12-06
            • 2012-11-12
            • 2011-09-23
            • 1970-01-01
            • 2013-01-08
            • 2015-03-24
            • 2013-12-07
            相关资源
            最近更新 更多