位移零位是否正常工作?

sha*_*oth 16 c++ bit-manipulation bit-shift

说我有这样的功能:

inline int shift( int what, int bitCount )
{
    return what >> bitCount;
}
Run Code Online (Sandbox Code Playgroud)

它将从不同的站点调用,每次bitCount都是非负的并且在位数内int.我特别关注bitCount等于零的呼叫- 它会正常工作吗?

还有可能编译器在编译其调用站点时看到函数的整个代码会减少bitCount等于零的调用到无操作吗?

pli*_*nth 28

根据K&R "如果右操作数为负数,或者大于或等于左表达式类型中的位数,则结果未定义." (A.7.8)因此>> 0是身份右移并且完全合法.


Tho*_*day 18

可以肯定的是,至少有一个C++编译器会识别这种情况(当0在编译时已知)并使其成为无操作:

资源

inline int shift( int what, int bitcount)
{
  return what >> bitcount ;
}

int f() {
  return shift(42,0);
}
Run Code Online (Sandbox Code Playgroud)

编译器开关

icpc -S -O3 -mssse3 -fp-model fast=2 bitsh.C
Run Code Online (Sandbox Code Playgroud)

英特尔C++ 11.0汇编

# -- Begin  _Z1fv
# mark_begin;
       .align    16,0x90
        .globl _Z1fv
_Z1fv:
..B1.1:                         # Preds ..B1.0
        movl      $42, %eax                                     #7.10
        ret                                                     #7.10
        .align    16,0x90
                                # LOE
# mark_end;
        .type   _Z1fv,@function
        .size   _Z1fv,.-_Z1fv
        .data
# -- End  _Z1fv
        .data
        .section .note.GNU-stack, ""
# End
Run Code Online (Sandbox Code Playgroud)

正如您在..B1.1中看到的那样,英特尔将"返回班次(42,0)"编译为"返回42".

英特尔11还剔除了这两种变化的转变:

int g() {
  int a = 5;
  int b = 5;
  return shift(42,a-b);
}

int h(int k) {
  return shift(42,k*0);
}
Run Code Online (Sandbox Code Playgroud)

对于在编译时移位值不可知的情况......

int egad(int m, int n) {
  return shift(42,m-n);
}
Run Code Online (Sandbox Code Playgroud)

......转变无法避免......

# -- Begin  _Z4egadii
# mark_begin;
       .align    16,0x90
        .globl _Z4egadii
_Z4egadii:
# parameter 1: 4 + %esp
# parameter 2: 8 + %esp
..B1.1:                         # Preds ..B1.0
        movl      4(%esp), %ecx                                 #20.5
        subl      8(%esp), %ecx                                 #21.21
        movl      $42, %eax                                     #21.10
        shrl      %cl, %eax                                     #21.10
        ret                                                     #21.10
        .align    16,0x90
                                # LOE
# mark_end;
Run Code Online (Sandbox Code Playgroud)

...但至少它是内联的,因此没有呼叫开销.

奖金组装:挥发性很贵.来源 ...

int g() {
  int a = 5;
  volatile int b = 5;
  return shift(42,a-b);
}
Run Code Online (Sandbox Code Playgroud)

...而不是无操作,编译为......

..B3.1:                         # Preds ..B3.0
        pushl     %esi                                          #10.9
        movl      $5, (%esp)                                    #12.18
        movl      (%esp), %ecx                                  #13.21
        negl      %ecx                                          #13.21
        addl      $5, %ecx                                      #13.21
        movl      $42, %eax                                     #13.10
        shrl      %cl, %eax                                     #13.10
        popl      %ecx                                          #13.10
        ret                                                     #13.10
        .align    16,0x90
                                # LOE
# mark_end;
Run Code Online (Sandbox Code Playgroud)

...所以,如果你在一台机器上工作,当你弹出它们时,你在堆栈上推送的值可能会不一样,那么这个错过的优化可能是你遇到的麻烦最少的.

  • 这并没有回答移位零是否是定义的行为。 (3认同)