【问题标题】:C Programming Setting Bits in Unsigned Char ArrayC 编程设置无符号字符数组中的位
【发布时间】:2016-05-01 21:42:59
【问题描述】:

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

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

感谢您的帮助。

【问题讨论】:

  • 1) ~0 不会将值设置为全零,而是全一。 2) 不要使用幻数。如果您指的是数组的大小,请使用sizeof 数组!对有符号整数进行位移时要小心(你很好地使用它们!)。
  • memset(array, ~0, 5); 将所有位设置为1(可能)。要设置为 0,请使用 0 而不是 ~0。或者最好写unsigned char chararray[5] = { 0 };

标签: c arrays char bitwise-operators bits


【解决方案1】:

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

(其中szarr 是您的char arr[5] = {0}; 数组的sizeof *arr(或只是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);
}

最后,您如何轻松查看每个无符号值的二进制表示来确认您的逻辑。与其他任何事情一样,您如何实现这一点也有很多变化。对于一般测试,我找到了一个简单的函数,它返回二进制字符串的char * 表示,填充到所需的长度,比几乎任何其他方法有用,因为它允许您保留正常的格式控制在单个printf 等内。

虽然unsigned 值不是 100% 必要的,但大多数硬件都同意 4-bytes 构成 intunsigned。但是,由于可能存在变化,因此最好使用精确类型或使用简单的预处理器指令来确定 BITS_PER_LONG 的数量等。使用将处理填充 long unsigned 值的通用二进制打印例程,一个简单的测试在 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;
}

使用/输出示例

输出值适用于您的 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

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

【讨论】:

  • 感谢您的详细回复!
  • 当然,很高兴为您提供帮助。 C语言中的每个问题总是有几种方法可以解决。每种方法都有一些东西需要学习。祝你编码顺利。
【解决方案2】:

每8位之后

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

【讨论】:

    【解决方案3】:

    简单版:

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

    应该有可能(使用各种技巧,并依靠一点“实现定义的”行为)创建一个更快的版本,一次处理 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;
    }
    

    【讨论】:

    • 对于简单版本,这样做:for (int i = 0; i &lt; 40; i++) chararray[i/8] |= ((intarray[i] == 1) &lt;&lt; (i%8)); 消除了循环中额外的条件分支,并将函数大小减少了 16 个字节 [on x86]。然而,没有替补,所以这可能是一个有争议的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-27
    • 2015-10-18
    相关资源
    最近更新 更多