我想要注意的是,正如Olaf所指出的那样,编译器没有错。
免责声明:我不完全确定这种行为是由编译器优化引起的。
无论如何,在C中我试图确定8位字节的第n位(n应该在0和7之间,包括0和7)是1
还是0
。我最初想出了这个解决方案:
#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;
}
根据我之前的理解,将对字节执行以下操作:
假设t = 5
和n = 2
。然后,字节t
可以表示为0000 0101
。我假设(t << (n - (n % 8) - 1))
会改变t
的位,以便t
为1010 0000
。这个假设只是有些正确。我还假设下一位移位(>> 7
)会移位t
的位,以便t
为0000 0001
。这个假设也只是有些正确。
TL; DR :我认为第return (t << (n - (n % 8) - 1)) >> 7;
行是这样做的:
t
是0000 0101
t
现在是1010 0000
t
现在是0000 0001
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;
}
我知道添加uint8_t val
并不是一个巨大的性能消耗。不过,我想知道两件事:
我的印象是,当编译器优化我的代码时,它会将两个位移一起打破,因此只发生一个。这似乎是一件好事,但它没有按照预期“清除”其他位。
答案 0 :(得分:7)
这个代码非常复杂,只是检查一个整数位。尝试标准方法:
return (t & (1U << n)) != 0;
如果必须检查n是否有效,请添加断言。 else掩蔽(n & 7)
或模数(n % 8)
(这将由编译器优化到掩码操作)将强制移位计数在有效范围内。由于该模式将被许多编译器识别,如果可用,它们可能会将其转换为单个位测试CPU指令。
为避免幻数,您应该将模数8
替换为:(sizeof(t) * CHAR_BIT)
。这将遵循t
可能具有的任何类型。掩模总是小于模数。
您的代码:
(n - (n % 8) - 1))
如果n < 8
它会产生负值(-1
正好)。 Negative shifts现在undefined behaviour,所以任何事情都可能发生(注意鼻子恶魔)。
答案 1 :(得分:0)
我相信你是整数推广的牺牲品。
当你有一个表达式:x operator y
时,你应该注意一些事情。第一个是表达式的结果(并且在其他操作数的过程中)被提升为&#34;最大的&#34;两个操作数的类型。
在您的示例中,这意味着以下内容:
(t << (n - (n % 8) - 1)) >> 7;
常数8是int
因此n%8
也是int
。(t << (n - (integer) - 1)) >> 7
(n - integer - 1)
也是一个整数,这意味着临时值(t << integer)
将存储在int
中。这意味着你不会切断&#34;你想要的最重要的一点,因为结果存储在(很可能)32位,而不像你想象的那样存储。另一方面,如果您暂时将int
结果存储在uint8_t
中,您将正确地切断前导位并获得您想要的结果。
您可以通过在计算过程中将操作数转换为uint8_t来解决此问题:
(t << (uint8_t)(n - (n % 8) - 1))) >> 7;
或者甚至更好,使用奥拉夫在答案中建议的面具:
(t & ((uint8_t)1 << n)) != 0