C编程设置无符号字符数组中的位

时间:2016-05-01 21:42:59

标签: c arrays char bitwise-operators bits

如何在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++;
}

我认为这是正确的,但我如何按顺序显示每个位,以便我可以看到它们是否已正确设置?

感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

每8位后

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

答案 1 :(得分:1)

简单版本:

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

应该可以(使用各种技巧,并依赖于一些&#34;实现定义&#34;行为)来创建一个一次执行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;
}

答案 2 :(得分:1)

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

最后,您如何轻松查看每个无符号值的二进制表示以确认您的逻辑。与其他任何事情一样,你可以通过多种方式实现这一目标。对于一般测试,我找到一个简单的函数,返回二进制字符串的char *表示,填充到所需的长度,或者更多,比任何其他方法更有用,因为它允许你保持正常单个printf等格式化控件。

虽然unsigned值不是100%必需,但大多数硬件同意4-bytes构成intunsigned。但是,由于可能存在变化,因此最好使用精确类型或使用简单的预处理程序指令来确定BITS_PER_LONG的数量等。使用通用二进制打印例程来处理填充{{1值,一个简单的测试在32/64位机器之间是有用的。 e.g:

long unsigned

现在将所有部分放在一起并组成/* 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-1的数组用于测试目的,您可以执行以下操作,并使用简单的编译器定义1表示使用暴力嵌套循环版本构建,或者如果没有给出定义则使用展开版本构建:

NESTED

示例使用/输出

输出值是针对您的5个#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; } 数组元素中的每一个,显示根据unsigned char-1的解释创建的十进制值s以及填充到1的该数字的二进制表示:

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
只需要

。 (当然根据自己的喜好调整输入和输出名称。)