【问题标题】:C/C++ Bit Array or Bit VectorC/C++ 位数组或位向量
【发布时间】:2011-06-03 23:57:51
【问题描述】:

我正在学习 C/C++ 编程并遇到过“位数组”或“位向量”的用法。无法理解他们的目的?这是我的疑问 -

  1. 它们是否用作布尔标志?
  2. 可以使用int 数组代替吗? (当然更多的记忆,但是..)
  3. Bit-Masking 是什么概念?
  4. 如果位掩码是简单的位操作以获得适当的标志,如何为它们编程?在 head 中执行此操作以查看与十进制数字相对应的标志是否不难?

我正在寻找应用程序,以便我可以更好地理解。例如 -

Q.给你一个文件,其中包含范围(1 到 100 万)内的整数。有一些重复,因此缺少一些数字。寻找寻找失踪者的最快方法 数字?

对于上述问题,我已经阅读了告诉我使用位数组的解决方案。如何存储每个整数?

【问题讨论】:

  • 顺便说一句,这是 C/C++ 不起作用的一个领域。 C++ 有位向量,而 C 没有。在 C 中,您必须自己编写。请养成将 C/C++ 分为 C 或 C++ 的习惯。

标签: c++ c bit bitarray bitvector


【解决方案1】:

我认为您对数组和数字感到困惑,特别是操作二进制数的含义。

我将通过示例来解决这个问题。假设您有许多错误消息,并且希望将它们作为函数的返回值返回。现在,您可以将错误标记为 1、2、3、4...,这在您的脑海中是有意义的,但是您如何根据一个数字确定发生了哪些错误?

现在,尝试将错误标记为 1、2、4、8、16... 基本上是增加 2 的幂。为什么这行得通?好吧,当您以 2 为基数时,您正在处理一个像 00000000 这样的数字,其中每个数字对应于 2 的幂乘以它从右边开始的位置。因此,假设发生错误 1、4 和 8。那么,那可以表示为00001101。反过来,第一位=1*2^0,第三位1*2^2,第四位1*2^3。把它们加起来就是 13。

现在,我们可以通过应用位掩码来测试是否发生了此类错误。例如,如果您想确定错误 8 是否发生,请使用 8 = 00001000 的位表示。现在,为了提取是否发生了该错误,请使用二进制文件,如下所示:

  00001101
& 00001000
= 00001000

我相信您知道 and 是如何工作的,或者可以从上面推断出它 - 以数字方式工作,如果任何两个数字都是 1,则结果为 1,否则为 0。

现在,在 C 中:

int func(...)
{
    int retval = 0;

    if ( sometestthatmeans an error )
    {
        retval += 1;
    }


    if ( sometestthatmeans an error )
    {
        retval += 2;
    }
    return retval
}

int anotherfunc(...)
{
    uint8_t x = func(...)

    /* binary and with 8 and shift 3 plaes to the right
     * so that the resultant expression is either 1 or 0 */
    if ( ( ( x & 0x08 ) >> 3 ) == 1 )
    {
        /* that error occurred */
    }
}

现在,实用性。当内存稀疏且协议没有冗长的 xml 等奢侈时,通常将字段分隔为这么多位宽。在该字段中,您将各种位(标志、2 的幂)分配给特定含义,并应用二进制运算来推断它们是否已设置,然后对这些位进行操作。

我还应该补充一点,二进制操作在概念上与计算机的底层电子设备很接近。想象一下,如果位域对应于各种电路的输出(载流与否)。通过使用足够多的上述电路组合,您就可以制造出……一台计算机。

【讨论】:

    【解决方案2】:

    关于位数组的使用:

    如果您知道“只有”100 万个数字 - 您使用 100 万位的数组。一开始,所有位都将为零,每次您读取一个数字时 - 将此数字用作索引并将此索引中的位更改为 1(如果它还不是 1)。

    读取所有数字后 - 缺失的数字是数组中零的索引。

    例如,如果我们只有 0 到 4 之间的数字,则数组的开头将如下所示:0 0 0 0 0。 如果我们阅读数字:3、2、2 数组如下所示: read 3 --> 0 0 0 1 0. read 3 (again) --> 0 0 0 1 0. read 2 --> 0 0 1 1 0. 检查零的索引: 0,1,4 - 这些是缺失的数字

    顺便说一句,您当然可以使用整数而不是位,但可能需要(取决于系统)32 倍内存

    西文

    【讨论】:

    • in the beginning all bits will be zero不知何故感动了我。
    • 所以基本上位数组是存储位的数据结构(而不​​是 int 或 char 数组)。这意味着,位数组只能用于需要开/关(或标志)类型应用的地方
    • 是的。唯一的区别是大小。但我只会在我想节省内存或需要将其存储在固定大小的空间中时使用它(如嵌入式寄存器\变量如 int\特定地址等...)
    【解决方案3】:

    位数组或位向量可以作为一个布尔值数组。通常布尔变量需要至少一个字节存储,但在位数组/向量中只需要一位。 如果您有大量此类数据,这会很方便,因此您可以节省大量内存。

    另一种用法是,如果您的 数字不完全适合标准变量,其大小为 8、16、32 或 64 位。您可以通过这种方式将一个由 4 位、一个 2 位和一个 10 位组成的数字存储到 16 位的位向量中。通常您必须使用 3 个大小分别为 8,8 和 16 位的变量,因此您只浪费了 50% 的存储空间。

    但所有这些用途都很少用于业务应用程序,通常在通过 pinvoke/interop 功能连接驱动程序和进行 低级编程 时使用。。 p>

    【讨论】:

      【解决方案4】:

      位向量的位数组用作从位置到某个位值的映射。是的,它与 Bool 数组基本相同,但典型的 Bool 实现是 1 到 4 个字节长,并且占用了太多空间。

      我们可以通过使用单词数组和二进制掩码操作以及转移来存储和检索它们来更有效地存储相同数量的数据(使用更少的整体内存,更少的内存访问,更少的缓存未命中,更少的内存页面交换) .访问单个位的代码仍然非常简单。

      C 语言中还内置了一些位字段支持(您可以编写 int i:1; 之类的内容来表示“仅消耗一位”),但它不适用于数组,并且您对整体结果的控制较少(详情实现取决于编译器和对齐问题)。

      以下是回答“搜索缺失号码”问题的一种可能方式。我将 int 大小固定为 32 位以保持简单,但可以使用 sizeof(int) 编写它以使其可移植。而且(取决于编译器和目标处理器)代码只能使用>> 5 而不是/ 32& 31 而不是% 32 来提高速度,但这只是为了给出想法。

      #include <stdio.h>
      #include <errno.h>
      #include <stdint.h>
      
      int main(){
          /* put all numbers from 1 to 1000000 in a file, except 765 and 777777 */
          {
              printf("writing test file\n");
              int x = 0;
              FILE * f = fopen("testfile.txt", "w");
              for (x=0; x < 1000000; ++x){
                  if (x == 765 || x == 777760 || x == 777791){
                      continue;
                  }
                  fprintf(f, "%d\n", x);
              }
              fprintf(f, "%d\n", 57768); /* this one is a duplicate */
              fclose(f);
          }
      
          uint32_t bitarray[1000000 / 32];
      
          /* read file containing integers in the range [1,1000000] */
          /* any non number is considered as separator */
          /* the goal is to find missing numbers */
          printf("Reading test file\n");
          {
              unsigned int x = 0;
              FILE * f = fopen("testfile.txt", "r");
              while (1 == fscanf(f, " %u",&x)){
                  bitarray[x / 32] |= 1 << (x % 32);
              }
              fclose(f);
          }
          /* find missing number in bitarray */
          {
              int x = 0;
              for (x=0; x < (1000000 / 32) ; ++x){
                  int n = bitarray[x];
                  if (n != (uint32_t)-1){
                      printf("Missing number(s) between %d and %d [%x]\n",
                          x * 32, (x+1) * 32, bitarray[x]);
                      int b;
                      for (b = 0 ; b < 32 ; ++b){
                          if (0 == (n & (1 << b))){
                              printf("missing number is %d\n", x*32+b);
                          }
                      }
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        用于位标志存储,以及解析不同的二进制协议字段,其中 1 个字节被划分为多个位字段。这在 TCP/IP、ASN.1 编码、OpenPGP 数据包等协议中得到了广泛应用。

        【讨论】:

          猜你喜欢
          • 2021-01-13
          • 1970-01-01
          • 1970-01-01
          • 2011-02-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-10
          相关资源
          最近更新 更多