在C语言中迭代位(bit)

10

我有一个大的char *str,其中前8个字符(如果我没记错的话等于64位)表示位图。有没有办法遍历这8个字符并查看哪些位是0?我很难理解比特概念,因为你无法在代码中“看到”它们,所以我无法想出任何方法来执行此操作。


3
建议展示“前8个字符”的样本。你指的是“前8个字符”和“这4个字符”是什么意思? - chux - Reinstate Monica
4 只是一个打字错误。当我说前8个字符时,我的意思是 str[1,2,...,8]。 - user16655
3
你的意思是0...7而不是1...8吧?因为在C语言中,数组下标从0开始。 - deviantfan
可能是如何在C/C++中设置、清除和切换单个位?的重复问题。 - Tyler Durden
6个回答

16

想象一下你只有一个字节,一个单独的字符my_char。你可以使用位运算符和位移来测试各个位。

unsigned char my_char = 0xAA;
int what_bit_i_am_testing = 0;

while (what_bit_i_am_testing < 8) {
  if (my_char & 0x01) {
     printf("bit %d is 1\n", what_bit_i_am_testing);
  }
  else {
     printf("bit %d is 0\n", what_bit_i_am_testing);
  }

  what_bit_i_am_testing++;
  my_char = my_char >> 1;
}

对你来说可能比较新的部分是 >> 运算符,该运算符将“在左侧插入零并将每个位向右移动,最右侧的位将被丢弃”。

这并不是一个非常技术性的描述,它表示将数字向右移动1位。


4
强调一下,为了使此代码对所有值都能正确运行(而不是对某些值永远运行),必须将my_char声明为unsigned - barak manos
是的,你说得对。但由于他说他的上下文是位图图像,为了清晰明了,我省略了所有这些考虑因素。太多的信息可能会让新手感到困惑。 - Vinicius Kamakura
谢谢您的回答。为什么要使用 my_char & 0x01? - user16655
这是重要的部分,它是位掩码。请阅读http://en.wikipedia.org/wiki/Mask_(computing),上面已经阐述得很好了。 - Vinicius Kamakura
这是一种破坏性的解决方案,如果 OP 想要读取位图的位,这是不合适的。 - Jean-Baptiste Yunès
2
@Jean-BaptisteYunès 为什么不合适?my_char 只是一个临时变量... my_char = my_bitmap[1234]; - Vinicius Kamakura

5
以下是逐位迭代无符号整数的方法(使用无符号整数而不是有符号整数以获得良好定义的行为;任何宽度的无符号整数都应该可以),一次迭代一个位。
定义以下宏:
#define LSBIT(X)                    ((X) & (-(X)))
#define CLEARLSBIT(X)               ((X) & ((X) - 1))

接下来您可以使用以下成语来迭代集合位,从最低位开始:

unsigned temp_bits;
unsigned one_bit;

temp_bits = some_value;
for ( ; temp_bits; temp_bits = CLEARLSBIT(temp_bits) ) {
    one_bit = LSBIT(temp_bits);
    /* Do something with one_bit */
}

我不确定这是否符合您的需求。您说您想检查0位,而不是1位——也许您可以对初始值进行按位取反操作。另外,对于多字节值,您可以将其放入另一个for循环中,以逐个处理一个字节/字。


3
在C语言中,char类型是8位宽字节,在计算机科学中,数据通常是以字节作为基本单位进行组织的。
在某些情况下,比如你的问题中,数据以布尔值存储在单个位中,因此我们需要一种方法来确定特定字节中特定位的状态。已经有一个SO解决方案关于如何在C语言中进行位操作
检查位的通常方法是将其与要检查的位进行AND运算:
int isBitSet = bitmap & (1 << bit_position);

如果执行此操作后变量 isBitSet 的值为 0,则该位未被设置。任何其他值都表示该位已打开。

s/8-bit wide/at least 8-bit wide - The Paramagnetic Croissant
1
在C语言中,字符是CHAR_BIT宽的字节。 CHAR_BIT至少为8。 - chux - Reinstate Monica
@chux 现代唯一具有超八位字节的系统是高度专业化的嵌入式系统。现代没有超八位的通用计算架构,因此从实际角度来看,char始终为8位。 - Tyler Durden
@Tyler Durden 1) 这个问题深入探讨了罕见的CHAR_BIT != 8的现状。2)由于C语言不要求新系统使用CHAR_BIT == 8,未来的系统可能会使用超级八位组char - chux - Reinstate Monica
@Tyler Durden 3) 就像2014年的系统一样,绝大多数使用2的补码来表示int,因此int溢出应该是明确定义的。由于C规范将int溢出定义为未定义以适应那些老旧的、令人讨厌的符号-幅度、1的补码、填充整数,更聪明的编译器利用了这一点,并创建了破坏先前依赖于明确定义的2的补码溢出的代码。为什么程序员们会依赖于明确定义的2的补码溢出——因为“所有”现代系统都使用2的补码。 - chux - Reinstate Monica
@Tyler Durden 3b) 同样地,精明的编译器可能会利用 CHAR_BIT 可能大于8的优势进行一些未来的优化。如果代码需要一个8位整数,请建议使用 (u)int8_t - chux - Reinstate Monica

3

这在小端内存架构中是正确的:

const int cBitmapSize = 8;
const int cBitsCount = cBitmapSize * 8;
const unsigned char cBitmap[cBitmapSize] = /* some data */;

for(int n = 0; n < cBitsCount; n++)
{
  unsigned char Mask = 1 << (n % 8);
  if(cBitmap[n / 8] & Mask)
  {
    // if n'th bit is 1...
  }
}

2
而且对于大端字节序,为什么要提到它呢?字节序只与较大的单元(shorts、整数和更大的单元)内部字节的排序有关。比特顺序非常幸运地在大端、中端和小端系统中都是相同的。 - Jongware

2

对于一个字符b,您可以这样简单地迭代:

for (int i=0; i<8; i++) {
  printf("This is the %d-th bit : %d\n",i,(b>>i)&1);
}

你可以根据需要遍历字符。
你应该明白的是,你不能直接操作位,只能使用数字在二进制下的一些算术属性来计算某些位表示的数字。
例如,它是如何工作的?一个字符有8个位。一个字符可以看作是一个用8个位表示的二进制数。如果b中的数字为b7b6b5b4b3b2b1b0(每个数字都是一个数字),那么b>>i就是将b向右移动i个位置(左侧填充0)。因此,10110111 >> 2是00101101,然后操作&1会隔离出最后一位(按位与运算符)。

3
现在你已经修复了它,我建议包括<limits.h>并将8更改为CHAR_BIT - barak manos
1
顺便说一下,如果你有一个char b等于二进制值10110111,并且你执行b >> 2,你得到的是11101101,而不是00101101。这是因为默认情况下charsigned char,当对signed变量进行右移操作时,符号位会跟随向右移动。要使b >> 2产生00101101,你必须声明unsigned char b - barak manos
我不想显得太过苛求。他只需要有关位操作的基本建议。 - Jean-Baptiste Yunès
3
在这里不要在追求学究式上吝啬,尤其是如果只需要添加几行信息。否则,提问者(以及未来阅读这个答案的其他用户)将会遇到不同的问题。 - barak manos

0

如果你想遍历所有字符。

char *str = "MNO"; // M=01001101, N=01001110, O=01001111
int bit = 0;

for (int x = strlen(str)-1; x > -1; x--){ // Start from O, N, M
    
    printf("Char %c \n", str[x]);
 
    for(int y=0; y<8; y++){ // Iterate though every bit
    // Shift bit the the right with y step and mask last position
        if( str[x]>>y & 0b00000001 ){ 
            printf("bit %d = 1\n", bit);
        }else{
            printf("bit %d = 0\n", bit);
        }
        bit++;
    }
    
}

输出

Char O
bit 0 = 1
bit 1 = 1
bit 2 = 1
bit 3 = 1
bit 4 = 0
bit 5 = 0
bit 6 = 1
bit 7 = 0
Char N 
bit 8 = 0
bit 9 = 1
bit 10 = 1
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接