简体   繁体   English

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

[英]Toggle a given range of bits of an unsigned int in C

I am trying to replace the following piece of code 我试图替换下面的代码

// 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++);

with the following one without a loop 以下一个没有循环

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

It turns out that in code version 1, time = 522240; 事实证明,在代码版本1中, time = 522240; in code version 2, time = 0; 在代码版本2中, time = 0; when I use 我用的时候

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

to compare the result. 比较结果。 I would like to know why is this happening and if there is any faster way to toggle bits in a given range. 我想知道为什么会发生这种情况,以及是否有更快的方法来切换给定范围内的位。 My compiler is gcc (Debian 4.9.2-10) 4.9.2. 我的编译器是gcc(Debian 4.9.2-10)4.9.2。

Edit: 编辑:

Thank you for your replies. 谢谢您的回复。 I have made a silly mistake and I feel embarrassing posting my question without further inspecting my codes. 我犯了一个愚蠢的错误,在没有进一步检查我的代码的情况下发布我的问题我感到很尴尬。 I did 我做到了

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

I am so sorry for any inconvenience incurred. 如有任何不便,我深表歉意。

Remark: both time_stx and time_enx are generated in the run-time and are not fixed. 备注: time_stxtime_enx都是在运行时生成的,并且不是固定的。

As suggested that I made a mistake and the problem is solved now. 建议我犯了一个错误,问题现在解决了。 Thank you!! 谢谢!!

Read Bit twiddling hacks . 阅读比特杂乱无章的黑客 Even if the answer isn't in there, you'll be better educated on bit twiddling. 即使答案不在那里,你也会受到更好的教育。 Also, the original code is simply setting the bits in the range; 此外,原始代码只是设置范围内的位; toggling means turning 1 bits into 0 bits and vice versa (normally achieved using ^ or xor). 切换意味着将1位转换为0位,反之亦然(通常使用^或xor实现)。

As to the code, I converted three variants of the expression into the following C code: 至于代码,我将表达式的三个变体转换为以下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;
}

When I look at the assembler for it (GCC 5.1.0 on Mac OS X 10.10.3), I get: 当我查看它的汇编程序(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

That's an amazingly large collection of labels! 这是一个非常大的标签集合!

The compiler has fully evaluated all three minimal bit_setterN() functions and inlined them, along with the call to print , into the body of main() . 编译器已经完全评估了所有三个最小的bit_setterN()函数,并将它们与print的调用一起内联到main()的主体中。 That includes evaluating the expressions to 522240 each time. 这包括每次评估表达式为522240。

Compilers are good at optimization. 编译器擅长优化。 Write clear code and let them at it, and they will optimize better than you can. 写清楚代码并让它们在它上面,它们将比你更好地优化。 Clearly, if the 11 and 19 are not fixed in your code (they're some sort of computed variables which can vary at runtime), then the precomputation isn't as easy (and bit_setter3() is a non-starter). 显然,如果你的代码中没有修复11和19(它们是某种在运行时可能会有所不同的计算变量),那么预计算就不那么容易了(并且bit_setter3()是非启动程序)。 Then the non-loop code will work OK, as will the loop code. 然后非循环代码将正常工作,循环代码也将如此。

For the record, the output is: 为了记录,输出是:

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

If your Debian compiler is giving you a zero from one of the code fragments, then there's either a difference between what you compiled and what you posted, or there's a bug in the compiler. 如果你的Debian编译器从一个代码片段给你一个零,那么你编译的内容和发布的内容之间存在差异,或者编译器中存在错误。 On the whole, and no disrespect intended, it is more likely that you've made a mistake than that the compiler has a bug in it that shows up in code as simple as this. 总的来说,没有任何不尊重的意图,你更有可能犯了一个错误,而不是编译器中有一个错误,它在代码中显示就像这样简单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM