在C

时间:2015-06-14 23:48:01

标签: 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中,代码版本2中为time = 522240;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都是在运行时生成的,并且不是固定的。

建议我犯了一个错误,问题现在解决了。谢谢!!

1 个答案:

答案 0 :(得分:2)

阅读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编译器从一个代码片段中给你一个零,那么你编译的内容和发布的内容之间存在差异,或者编译器中存在错误。总的来说,没有任何不尊重的意图,你更有可能犯了一个错误,而不是编译器中有一个错误,它在代码中显示如此简单。