我真的不知道为什么移位在一种情况下有效但在另一种情况下无效

I don't really know why bit-shifting works in one scenario but not the other

所以我正在做一些位移,我遇到了以下问题,如果能得到答案,我将不胜感激:

作为参数,我可以传递 1 个字节的大小。

前4位代表一个分子。 最后4位代表分母。

以下代码有效并给出正确的输出:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(int argc, char** argv)
{
    for(int i = 1; i < argc; i++)
    {
        unsigned char numerator = atoi(argv[i]);
        numerator = (numerator >> 4);
        numerator = (numerator << 4);

        unsigned char denominator = atoi(argv[i]);
        denominator = (denominator << 4);
        denominator = (denominator >> 4);
        printf("%d/%d\n", numerator, denominator);
    }

    return 0;
}

但是如果我像这样替换位移部分,分母给出与分子相同的输出:

unsigned char numerator = atoi(argv[i]);
        numerator = (numerator >> 4) << 4;

unsigned char denominator = atoi(argv[i]);
        denominator = (denominator << 4) >> 4;

示例输入为:

./test 1
./test 16

给出的输出:

0/1
16/16

预期输出:

0/1
16/0

在此先感谢您的帮助。

似乎是将值转换为更大的整数类型(例如int),并且在执行移位时,高位保留为更大的int类型,然后将结果转换回两个班次都完成后的 unsigned char。

我会使用这样的位掩码,而不是移位:

...
unsigned char data = atoi(argv[i]);
unsigned char numerator = data & 0xf0;
unsigned char denominator = data & 0xf;
...

问题案例是分母,变量的大小是分母的一个因素。在第一个版本中,您有:

    denominator = (denominator << 4);
    denominator = (denominator >> 4);

这会将值左移 4,然后将其截断以适合 unsigned char,然后右移结果。在 16 的情况下,第一个移位产生 256,然后在存储在分母中时将其截断为 0。然后右移只是移动 0.

在第二个版本中,您有:

    denominator = (denominator << 4) >> 4;

这会将其左移 4,但不会截断结果以适应 unsigned char,然后右移未截断的结果。在16的情况下,第一个移位产生256,然后右移回到16

要获得与第一个版本等效的一行,您可以这样做:

    denominator = (unsigned char)(denominator << 4) >> 4;

在 C 中计算算术表达式时,任何小于 int 的整数类型都会在执行计算之前提升为 int


所以这段代码:

unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4);
denominator = (denominator >> 4);

结果如下(每个方框代表4位,方框内的文字为16进制数字):

请注意,赋值 denominator = (denominator << 4); 会强制编译器将值转换回 unsigned char。然后在下一行代码中,编译器需要将 unsigned char 提升回 int.


但是这段代码:

unsigned char denominator = atoi(argv[i]);
denominator = (denominator << 4) >> 4;

跳过转换回 unsigned char 和第二次升级到 int。所以顺序是:

请注意,最终值与原始值相同,因为移位是使用 32 位执行的,并且没有从 32 位值的左侧移出任何内容。


分子没有同样的问题。因为分子先右移,所以右边的位丢失了。升级到 int 对结果没有影响。这是分子的顺序: