逐位 for-each 函数

bit-wise for-each function

我有一长串单位 uint32 数字。有时他们是||一起组成一个新的数字。对于这个新数字中的每个设置位,我需要执行特定的操作。所以基本上我正在编写一个循环来遍历数字的每一位。

我知道我可以用 while 循环轻松地做到这一点。但是因为我需要在 hte 代码的许多部分做同样的事情。我想我可以为它创建一个函数(就像 json_object_foreach() 函数一样)。

对于函数参数,我解析的是原始数,起始位的位置(假设是从第0位开始,如果没有意义,我也可以从1开始),还有什么该位的数值。

到目前为止我有以下内容。代码编译。但是代码不是从 0-31 迭代。我很确定 get_next_bit_position 功能不正确,我不知道如何修复它。

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

uint32_t get_value_of_kth_bit(uint32_t original_number, int bit_position)
{
    int result = 0;
    // if the bit_position is set to 1
    printf("original_number is: %u\n", original_number);
    printf("bit_position is: %d\n", bit_position);
    if (original_number & (1 << (bit_position))){
        // if the bit_position is set to 1
        // calculate the numerical value
        if(bit_position == 0){
            result = 1;
        }
        while (bit_position!=0){
            result*=2;
            --bit_position;
        }
        printf("result: %d\n", result);
        return result;
    } else {
        return 0;
    }
}

uint32_t get_next_bit_position(uint32_t bit_position)
{
    printf("bit_position input is : %u\n", bit_position);
    if(bit_position < 32){
        bit_position++;
    }
    printf("new bit_position is : %u\n", bit_position);
    return bit_position;
}

#define foreach_bit(original_number, bit_position, current_bit_value) \
    for(current_bit = 0; \
       current_bit_value = get_value_of_kth_bit(original_number, bit_position); \
       current_bit = get_next_bit_position(current_bit))

#define CLASS_A    0x00000001  // 0001
#define CLASS_B    0x00000002
#define CLASS_C    0x00000010  // 1010
#define CLASS_D    0x00000400
//...
// the list goes on 

int main(){

    uint32_t num = CLASS_A | CLASS_C; // 1011

    uint32_t current_bit = 0;
    uint32_t current_bit_value = 0;


    foreach_bit(num, current_bit, current_bit_value){
        printf("Entered function\n");

        if(current_bit_value = CLASS_A){
            printf("Found class_a bit\n");
            // do something else
        } 
        else if (current_bit_value = CLASS_B){
            printf("Found class_b bit\n");
            // do something else
        } 
        else if (current_bit_value = CLASS_D) {
            printf("Found class_c bit\n");
            // do something else
        }
    }
}

代码输出如下,这很奇怪。由于我认为函数应该在运行get_value_of_kth_bit

之前进入foreach_bit函数
original_number is: 17
bit_position is: 0
result: 1
Entered function
Found class_a bit
bit_position input is : 0
new bit_position is : 1
original_number is: 17
bit_position is: 1

关于要求的更新:

  1. 我知道总共有 32 个 1 位 uint32 数字的列表。让我们称之为 Class List
#define CLASS_A    0x00000001  // 0001
#define CLASS_B    0x00000002
#define CLASS_C    0x00000004  // 0100
#deifne CLASS_D    0x00000008
#define CLASS_E    0x00000010
#define CLASS_F    0x00000020
....
  1. 我会得到一个随机的 uint32 数字,我需要看看有多少以及哪些 Class 列出这个随机数字 (original_number) 包含。
  2. 我有一个 herlper 函数,它接受 Class 列表编号 (uint32) 并执行某些操作。例如get_class_name_str(uint32 class_number)

例如original_number为5(二进制:0101)。 我检查了第一位(最右边),即 1 (0001)。 0001 是 CLASS_A 的值。所以我看到 CLASS_A 在这个 original_number 中,我可以将它解析为辅助函数来做一些事情。 然后,它设置的第二位为0,这意味着CLASS_B不在这个original_number中。 第三位为1,表示CLASS_C是original_number的一部分。

我可以在 while 循环中执行此操作,以检查每一位。但是,我希望它本身就是一个函数,因为它被用在多个地方。

So basictly I'm writing a loop to iterat through each bit of the number.

我认为你把它复杂化了。只是从字面上迭代这些位,并排除未设置的位:

for (unsigned i = 0; i < 32; ++i) {
   uint32_t bit = 1u << i;
   if (!(num & bit)) continue;
   // use bit
}

我们可以在那个宏中压缩:

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

#define foreach_bit(BIT, ITER, NUM) \
    for (uint32_t BIT, ITER = 0; ITER < 32 && (BIT = 1u << ITER, 1); ++ITER) \
       if (!(NUM & BIT)) \
           continue; \
       else

int main() {
    uint32_t val = 0b1011;
    foreach_bit(i, _in, val) {
        printf("%x\n", i);
    }
}

代码片段输出:

1
2
8

然后我们甚至可以通过检查掩码在移动时是否为零来消除对位位置的需要 - 它只会是,当最后一位被采用时/下面看起来很不错,我想我更喜欢它:

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

#define foreach_bit(BIT, NUM) \
    for (uint32_t BIT = 1; BIT << 1; BIT <<= 1) \
       if (!(NUM & BIT)) \
           continue; \
       else

int main() {
    uint32_t val = 0b1011;
    foreach_bit(i, val) {
        printf("%x\n", i);
    }
}

另一个想法,您不需要真正的“位位置”- 您可以从输入中删除访问的位,从输入中设置的第一个位计算掩码:

#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#include <strings.h>

#define foreach_bit(IDX, STATE, VAL) \
    for (uint32_t STATE = VAL, IDX; \
        STATE ? (IDX = 1 << (ffsl((long)STATE) - 1)) : 0; \
        STATE &= ~IDX)

int main(){
    uint32_t num = 0b1011;
    foreach_bit(i, _in, num) {
        printf("%x\n", i);
    }
}

在您的代码中,您只检查一次 if (original_number & (1 << (bit_position))){ - 您必须在 while (bit_position!=0){ 循环中检查它并从上次访问的位开始检查。


无论如何,在提供的代码中编写任何循环都没有什么意义。只需检查位:

int main(){
        uint32_t num = CLASS_A | CLASS_C
        if (num & CLASS_A){
            printf("Found class_a bit\n");
            // do something else
        } 
        else if (num & CLASS_B){
            printf("Found class_b bit\n");
            // do something else
        } 
        else if (num & CLASS_D) {
            printf("Found class_c bit\n");
            // do something else
        }
}