反正有没有在C中绕过这个编译器优化?

4 c bit-shift bit compiler-optimization

我想要指出的是,正如Olaf所指出的,编译器没有错.


免责声明:我不完全确定这种行为是由编译器优化引起的.

不管怎么说,在C我试图一个8位字节,以确定是否第n位(n应在0和7之间,包括端点)是10.我最初想出了这个解决方案:

#include <stdint.h>
#include <stdbool.h>

bool one_or_zero( uint8_t t, uint8_t n ) // t is some byte, n signifies which bit
{
    return (t << (n - (n % 8) - 1)) >> 7;
}
Run Code Online (Sandbox Code Playgroud)

根据我之前的理解,这将对字节执行以下操作:

假设t = 5n = 2.然后该字节t可以表示为0000 0101.我认为(t << (n - (n % 8) - 1))会的位移位t,这样t1010 0000.这个假设只是有些正确.我还假设下一个比特移位(>> 7)将比特移位的t,从而t0000 0001.这个假设也只是有些正确.

TL; DR:我认为这条线return (t << (n - (n % 8) - 1)) >> 7;是这样做的:

  1. t0000 0101
  2. 发生第一次移位; t就是现在1010 0000
  3. 发生第二次移位; t就是现在0000 0001
  4. t 作为返回 0000 0001

虽然我打算这样做,但事实并非如此.相反,我必须写下以下内容,以获得我的预期结果:

bool one_or_zero( uint8_t t, uint8_t n ) // t is some byte, n signifies which bit
{
    uint8_t val = (t << (n - (n % 8) - 1));
    return val >> 7;
}
Run Code Online (Sandbox Code Playgroud)

我知道添加uint8_t val不是一个巨大的性能消耗.不过,我想知道两件事:

  1. 我是否必须初始化另一个变量来执行我想要的操作?
  2. 为什么单线轮与双线轮做同样的事情呢?

我的印象是,当编译器优化我的代码时,它会将两个位移一起打破,所以只发生一个.这似乎是一件好事,但它没有按照预期"清除"其他位.

too*_*ite 7

该代码非常复杂,只需检查一个整数位.尝试标准方法:

return (t & (1U << n)) != 0;
Run Code Online (Sandbox Code Playgroud)

如果必须检查n是否有效,请添加断言.否则掩蔽(n & 7)或模数(n % 8)(这将由编译器优化到掩码操作)将强制移位计数在有效范围内.由于该模式将被许多编译器识别,因此如果可用,它们可能会将其转换为单个位测试CPU指令.

为了避免幻数,你应该用以下方法替换模数8:(sizeof(t) * CHAR_BIT).这将遵循任何类型t可能具有的.掩模总是小于模数.

你的代码:

(n - (n % 8) - 1))
Run Code Online (Sandbox Code Playgroud)

如果n < 8它产生负值(-1精确地).负向变化呈现未定义的行为,因此任何事情都可能发生(注意鼻子恶魔).

  • @gragas:如果你提到一般的右移:阅读_integer promotion_!`unit8_t`将首先提升为`int`进行操作!但是,_negative_ shift以及与左操作数**相同或更大位的移位都是**_undefined_独立于signed-ness!阅读我包含的链接. (2认同)