c - 在 C 中切换 unsigned int 的给定范围的位

标签 c bit-manipulation

我正在尝试替换以下代码

// code version 1
unsigned int time_stx = 11; // given range start
unsigned int time_enx = 19; // given range end
unsigned int time     = 0;  // desired output

while(time_stx < time_enx) time |= (1 << time_stx++);

下面的没有循环

// code version 2
unsigned int time_stx = 11;
unsigned int time_enx = 19;
unsigned int time     = (1 << time_enx) - (1 << time_stx);

事实证明,在代码版本 1 中,time = 522240; 在代码版本 2 中,time = 0; 当我使用

printf("%u\n", time);

比较结果。我想知道为什么会发生这种情况,以及是否有更快的方法来切换给定范围内的位。我的编译器是 gcc (Debian 4.9.2-10) 4.9.2。

编辑:

谢谢您的回复。我犯了一个愚蠢的错误,在没有进一步检查我的代码的情况下发布我的问题让我感到尴尬。我做到了

unsigned int time_stx = 11;
unsigned int time_enx = 19;

unsigned int time1    = 0;
while(time_stx < time_enx) time1 |= (1 << time_stx++); // version 1

//// what I should, but forgotten to do
// time_stx = 11;
// time_enx = 19;

// where time_stx = time_enx now...
unsigned int time2    = (1 << time_enx) - (1 << time_stx); // version 2

// then obviously
printf("time1 = %u\n", time1); // time1 = 522240
printf("time2 = %u\n", time2); // time2 = 0

对于给您带来的任何不便,我们深表歉意。

备注:time_stxtime_enx都是在运行时生成的,不固定。

正如所建议的,我犯了一个错误,问题现在已经解决了。谢谢!!

最佳答案

阅读Bit twiddling hacks 。即使答案不在那里,你也会得到更好的关于位调整的教育。此外,原始代码只是简单地设置范围内的位;切换意味着将 1 位转换为 0 位,反之亦然(通常使用 ^ 或 xor 实现)。

至于代码,我将表达式的三种变体转换为以下 C 代码:

#include <stdio.h>

static void print(unsigned int v)
{
    printf("0x%.8X = %u\n", v, v);
}

static void bit_setter1(void)
{
    unsigned int time_stx = 11; // given range start
    unsigned int time_enx = 19; // given range end
    unsigned int time     = 0;  // desired output

    while (time_stx < time_enx)
        time |= (1 << time_stx++);

    print(time);
}

static void bit_setter2(void)
{
    unsigned int time_stx = 11;
    unsigned int time_enx = 19;
    unsigned int time     = (1 << time_enx) - (1 << time_stx);
    print(time);
}

static void bit_setter3(void)
{
    unsigned int time = 0xFF << 11;
    print(time);
}

int main(void)
{
    bit_setter1();
    bit_setter2();
    bit_setter3();
    return 0;
}

当我查看它的汇编程序(Mac OS X 10.10.3 上的 GCC 5.1.0)时,我得到:

        .globl _main
_main:
LFB5:
LM1:
LVL0:
        subq    $8, %rsp
LCFI0:
LBB28:
LBB29:
LBB30:
LBB31:
LM2:
        movl    $522240, %edx
        movl    $522240, %esi
        leaq    LC0(%rip), %rdi
        xorl    %eax, %eax
        call    _printf
LVL1:
LBE31:
LBE30:
LBE29:
LBE28:
LBB32:
LBB33:
LBB34:
LBB35:
        movl    $522240, %edx
        movl    $522240, %esi
        xorl    %eax, %eax
        leaq    LC0(%rip), %rdi
        call    _printf
LVL2:
LBE35:
LBE34:
LBE33:
LBE32:
LBB36:
LBB37:
LBB38:
LBB39:
        movl    $522240, %edx
        movl    $522240, %esi
        xorl    %eax, %eax
        leaq    LC0(%rip), %rdi
        call    _printf
LVL3:
LBE39:
LBE38:
LBE37:
LBE36:
LM3:
        xorl    %eax, %eax
        addq    $8, %rsp
LCFI1:
        ret

这是一个惊人的大标签集合!

编译器已完全评估所有三个最小的 bit_setterN() 函数,并将它们连同对 print 的调用一起内联到 main() 的主体中。这包括每次将表达式计算为 522240。

编译器擅长优化。编写清晰的代码并让他们执行,他们会比您优化得更好。显然,如果 11 和 19 在代码中没有固定(它们是某种计算变量,在运行时可能会变化),那么预计算就不那么容易了(并且 bit_setter3() 是一个不可能的人)。然后非循环代码将正常工作,循环代码也将正常工作。

郑重声明,输出为:

0x0007F800 = 522240
0x0007F800 = 522240
0x0007F800 = 522240

如果您的 Debian 编译器从某个代码片段中给您一个零,那么您编译的内容和您发布的内容之间可能存在差异,或者编译器中存在错误。总的来说,无意冒犯,更可能的是您犯了一个错误,而不是编译器中存在像这样简单的代码中显示的错误。

关于c - 在 C 中切换 unsigned int 的给定范围的位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30835711/

相关文章:

c++ - 挑战 - Arduino 返回多个数组

c++ - C/C++ Linux GDB API

c - 使用 -pg 编译的 gcc 不会生成 gprof 所需的二进制文件

c - 是否可以更快地对 0's 1' 求和?

c - 优化 32 位值构造

c - 凌乱的代码从下一个元素中删除了第一位追加

c - 为什么使用 calloc 将分配的内存初始化为零?

c - 为什么我的 D2XX 应用程序在 fork 时不起作用?

mysql - 哪个更好地控制状态和执行查询? 1 个 TINYINT 列、1 个 BIT(8) 列或 8 个 BIT(1) 列

c++ - 我如何在 C++ 中将 bitset 转换为 short?