C 编程设置 Unsigned Char 数组中的位

C Programming Setting Bits in Unsigned Char Array

如何在 c 语言中设置 unsigned char 数组的各个位?如果我有

 unsigned char chararray[5];
 memset(chararray, ~0, 5);

我将所有位设置为 0。然后我需要将某些位设置为 1。我知道此数组的每个元素中有 8 位,所以如果我要将第二个元素中的第 3 位设置为相信我需要做类似

的事情
chararray[1] |= (1 << 3);

我需要将数组的所有位设置为 1 以匹配另一个数组。例如,我有一个包含 1 和 -1 的 40 元素整数数组。如果索引 i 处的整数数组中的数字是 1,我想将字符数组中的位设置为 1,如果不是,则将该位保留为 0。

这是我目前的情况:

unsigned char chararray[5];
memset(array, ~0, 5);
int intarray[40]; //filled with random 1's and -1's
int j = 0;
for (int i = 0; i < 40; i++)
{
    if (intarray[i] == 1)
    {
        if(j == 0)
            chararray[0] |= (1 << i);
        else
            chararray[j] |= (1 << (i%8));
    }
    if(i % 8 == 0 && i > 0)
        j++;
}

我相信这是正确的,但我如何按顺序显示每一位,以便我可以查看它们是否已正确设置?

感谢您的帮助。

每8位后

for(int b=7;b>=0;b--)
{
   printf("%d", (chararray[j] & (1<<b)) ? 1 : 0);
}
printf("\n");

简单版:

for (int i = 0; i < 40; i++) {
    if (intarray[i] == 1) {
        chararray[i/8] |= (1 << (i%8) );
    }
}

应该有可能(使用各种技巧,并依靠一点点 "implementation defined" 行为)创建一个更快的版本,一次处理 8 个整数(并避免很多分支,并避免需要用零预填充字符);喜欢:

for (int i = 0; i < 40; i += 8) {
    temp = ((unsigned int)intarray[i]) >> 31;
    temp |= (((unsigned int)intarray[i+1]) >> 31) << 1;
    temp |= (((unsigned int)intarray[i+2]) >> 31) << 2;
    temp |= (((unsigned int)intarray[i+3]) >> 31) << 3;
    temp |= (((unsigned int)intarray[i+4]) >> 31) << 4;
    temp |= (((unsigned int)intarray[i+5]) >> 31) << 5;
    temp |= (((unsigned int)intarray[i+6]) >> 31) << 6;
    temp |= (((unsigned int)intarray[i+7]) >> 31) << 7;
    chararray[i/8] = ~temp;
}

如果你的整数包含 0 和 1(而不是 -1 和 1)会更有趣;因为那样你就可以摆脱这样的事情:

for (int i = 0; i < 40; i += 8) {
    temp = intarray[i];
    temp |= intarray[i+1] << 1;
    temp |= intarray[i+2] << 2;
    temp |= intarray[i+3] << 3;
    temp |= intarray[i+4] << 4;
    temp |= intarray[i+5] << 5;
    temp |= intarray[i+6] << 6;
    temp |= intarray[i+7] << 7;
    chararray[i/8] = temp;
}

如果我理解你问题的两个部分 (1) 从 40-element 整数数组 -1 & 1 值中确认你对 5 - unsigned char 值的位设置; (2) 输出每个 5 - unsigned char 值的二进制表示,然后可以通过多种方式完成。为了在每个 unsigned char 值中 set 位,你只需按位 OR 当前值与 1 左移 的适当的数量来设置有问题的位。例如要在 unsigned char v = 0; 中设置 3rd-bit,您只需设置 v |= 1<<2;

可以使用嵌套循环蛮力方法完成为所有 5 个 unsigned char 元素设置位的几种不同方法:

/* brute force nested loop approach */
for (i = 0; i < szarr; i++)
    for (j = 0; j < CHAR_BIT; j++)
        if (buf[i * CHAR_BIT + j] == 1) arr[i] |= (1 << j);

(其中 szarrsizeof *arr 对于您的 char arr[5] = {0}; 数组(或只是 5,并且 CHAR_BIT (limits.h) 定义char 中的位数(通常是 8))

一种在效率方面具有优势的展开方法(尽管大多数当前编译器将尝试采用积极优化的展开解决方案)。它更好地说明了发生的过程:

/* unrolled loop setting all bits in each unsigned char per loop */
for (i = 0; i < szbuf; i+=8) {
    if (buf[ i ] == 1) arr[i/8] |= 1;
    if (buf[i+1] == 1) arr[i/8] |= (1 << 1);
    if (buf[i+2] == 1) arr[i/8] |= (1 << 2);
    if (buf[i+3] == 1) arr[i/8] |= (1 << 3);
    if (buf[i+4] == 1) arr[i/8] |= (1 << 4);
    if (buf[i+5] == 1) arr[i/8] |= (1 << 5);
    if (buf[i+6] == 1) arr[i/8] |= (1 << 6);
    if (buf[i+7] == 1) arr[i/8] |= (1 << 7);
}

最后,您如何轻松查看每个无符号值的二进制表示以确认您的逻辑。与其他任何事情一样,您可以如何实现这一点。对于一般测试,我发现一个简单的函数,它 returns 二进制字符串的 char * 表示,填充到所需的长度,as 或更多,比它允许的任何其他方法都有用您可以在单个 printf 等范围内保留正常的格式控制。

虽然 unsigned 值并非 100% 必要,因为大多数硬件都同意 4-bytes 构成 intunsigned。但是,由于可能存在变化,因此最好使用精确类型或使用简单的预处理器指令来确定 BITS_PER_LONG 的数量等。使用将处理填充 [=42] 的通用二进制打印例程=] 值,一个简单的测试在 32/64 位机器之间很有帮助。例如:

/* BITS_PER_LONG */
#if defined(__LP64__) || defined(_LP64)
# define BITS_PER_LONG 64
#else
# define BITS_PER_LONG 32
#endif
...
/** returns pointer to binary representation of 'v' zero padded to 'sz'.
 *  returns pointer to string contianing binary representation of
 *  unsigned 64-bit (or less ) value zero padded to 'sz' digits.
 */
char *binpad (const unsigned long v, const size_t sz)
{
    static char s[BITS_PER_LONG + 1] = {0};
    char *p = s + BITS_PER_LONG;
    register size_t i;

    for (i = 0; i < sz; i++)
        *--p = (v>>i & 1) ? '1' : '0';

    return p;
}

现在将所有部分放在一起并组成一个 40-element 值数组,这些值是 -11 用于测试目的,您可以执行如下操作,并使用简单的编译器定义 NESTED 表示使用 强力嵌套循环 版本构建,或者如果没有给出定义则使用展开版本构建:

#include <stdio.h>

/* CHAR_BIT */
#ifndef CHAR_BIT
# define CHAR_BIT  8
#endif

/* BITS_PER_LONG */
#if defined(__LP64__) || defined(_LP64)
# define BITS_PER_LONG 64
#else
# define BITS_PER_LONG 32
#endif

char *binpad (const unsigned long v, const size_t sz);

int main (void){

    unsigned char buf[] = { -1, -1,  1, -1, -1, -1,  1,  1,
                            -1,  1, -1, -1, -1,  1,  1, -1,
                             1, -1, -1, -1,  1,  1, -1, -1,
                            -1, -1, -1,  1,  1, -1, -1, -1,
                            -1, -1,  1,  1, -1, -1, -1,  1 };
    unsigned char arr[5] = {0};
    unsigned i, szarr = (unsigned) sizeof arr;

#ifdef NESTED
    unsigned j;
    /* brute force nested loop approach */
    for (i = 0; i < szarr; i++)
        for (j = 0; j < CHAR_BIT; j++)
            if (buf[i * CHAR_BIT + j] == 1) arr[i] |= (1 << j);
#else
    unsigned szbuf = (unsigned) sizeof buf;
    /* unrolled loop setting all bits in each unsigned char per loop */
    for (i = 0; i < szbuf; i+=8) {
        if (buf[ i ] == 1) arr[i/8] |= 1;
        if (buf[i+1] == 1) arr[i/8] |= (1 << 1);
        if (buf[i+2] == 1) arr[i/8] |= (1 << 2);
        if (buf[i+3] == 1) arr[i/8] |= (1 << 3);
        if (buf[i+4] == 1) arr[i/8] |= (1 << 4);
        if (buf[i+5] == 1) arr[i/8] |= (1 << 5);
        if (buf[i+6] == 1) arr[i/8] |= (1 << 6);
        if (buf[i+7] == 1) arr[i/8] |= (1 << 7);
    }
#endif    

    for (i = 0; i < szarr; i++) /* validate the bit settings */
        printf (" arr[%2u] : %3u  (%s)\n",
                i, arr[i], binpad (arr[i], CHAR_BIT));

    return 0;
}

/** returns pointer to binary representation of 'v' zero padded to 'sz'.
 *  returns pointer to string contianing binary representation of
 *  unsigned 64-bit (or less ) value zero padded to 'sz' digits.
 */
char *binpad (const unsigned long v, const size_t sz)
{
    static char s[BITS_PER_LONG + 1] = {0};
    char *p = s + BITS_PER_LONG;
    register size_t i;

    for (i = 0; i < sz; i++)
        *--p = (v>>i & 1) ? '1' : '0';

    return p;
}

例子Use/Output

输出值适用于 5 个 unsigned char 数组元素中的每一个,显示根据 -11 以及该数字的二进制表示填充到 8-chars:

$ ./bin/array_uc_bits
 arr[ 0] : 196  (11000100)
 arr[ 1] :  98  (01100010)
 arr[ 2] :  49  (00110001)
 arr[ 3] :  24  (00011000)
 arr[ 4] : 140  (10001100)

查看所有答案和所有方法,如果您还有其他问题,请告诉我。

注意:如果您不熟悉如何在编译时设置像NESTED这样的标签定义,您需要做的就是将其作为选项传递-D,例如-DNESTED 在命令行上传递将触发编译嵌套暴力代码,例如

$ gcc -Wall -Wextra -Ofast -DNESTED -o bin/array_uc_bits_nest array_uc_bits.c

是所有需要的。 (当然可以根据自己的喜好调整输入和输出名称。)