【问题标题】:C - BitArray - Set single bit of uint64_tC - BitArray - 设置 uint64_t 的单个位
【发布时间】:2016-10-31 07:03:23
【问题描述】:

我目前正在做一个项目,我需要位组。我正在使用 uint64_t 的数组作为 bitset。

我目前的问题是,每当我想设置或检查一点时,我都需要做这样的操作:

uint64_t index = 42;
bArr[index/64] |= (((uint64_t)1)<<(index%64)); 

我也可以用一些巧妙的 and 和 bitshift 操作重写除法和取模,但我担心1 的演员阵容。我需要这个演员表,否则 1 被视为 32 位单元。正如在这个例子中看到的 - 你得到错误的输出没有演员:

uint64_t bArr[4];                           // 256 bits 
bArr[0] = bArr[1] = bArr[2] = bArr[3] = 0;  // Set to 0

uint64_t i = 255;
bArr[i/64] = (bArr[i/64] | (((uint64_t)1)<<(i%64))); 

uint32_t i2;
for (i2 = 0; i2 < 256; i2++) {
  if ((bArr[i2/64] & (((uint64_t)1)<<(i2%64))) != 0) {
    printf("bArray[%" PRIu32 "] = 1\n", i2);
  }
}

我可以巧妙地绕过这个演员阵容吗?我在想,性能可能会受到每个读/写时的演员阵容的影响......

【问题讨论】:

  • 不要不将除法和取模重写为“聪明”;编译器当然足够聪明,已经为您进行了这些优化。还可以考虑使用CHAR_BIT * sizeof bArr[0] 而不是64,以避免出现幻数。
  • @unwind 感谢您的提示。我会用我的代码测试它。不过,情况可能就是这样。
  • 如果您正在寻找速度,请提供一个 const uint64_t 表,其中包含 64 个不同的 ULL 常量(1 个预先移动到所有可能的位置)并对其进行索引。

标签: c casting bit-shift memory-efficient bitarray


【解决方案1】:

如果我理解正确,您需要一个至少 64 位长的文字 1。你可以通过写1ull 而不是只写1 来get this 而不需要任何演员。这会创建一个值为 1 的 unsigned long long 文字。但是,不能保证该类型不会超过 64 位,因此如果您依赖它正好是 64 位,那么您可能需要进行强制转换。

【讨论】:

    【解决方案2】:

    演员表本身不会影响性能。这是一个编译时构造,它告诉编译器表达式的类型。

    在这种情况下,它们都是整数转换和表达式,因此应该不会影响性能。

    【讨论】:

    • 哦,你又来了 :) 所以你可能记得这几乎是你的代码(仅限uint64_t)。您还知道数组uint64_t、uint32_t、uint16_t 甚至uint8_t 的最佳尺寸是多少?我脑子里有 L1/L2 缓存大小,但对它了解不多……
    • cast [...] 是一个编译时构造,它告诉编译器表达式的类型 说 cast 是编译时构造是不正确的大多数情况下不是。
    【解决方案3】:

    &lt;&lt; 运算符的结果类型是左操作数的类型(在整数提升之后),这就是您需要使用正确类型的原因:1 的类型为 int,但您需要类型为 uint64_t。

    你可以使用:

    (uint64_t) 1
    

    或

    UINT64_C(1)  /* you have to include <stdint.h> */
    

    或

    1ULL  
    

    (对于最后一个假设 unsigned long long 在您的系统中是 64 位的,这很有可能)。

    但是它们都是等价的:所有这些表达式都是整数常量表达式,它们的值是在编译时而不是在运行时计算的。

    【讨论】:

    • 很高兴知道,这发生在编译时并且不会影响性能(只会丑化代码)...
    • 详细信息 unsigned long long 至少是 64 位的,所以 bArr[index/64] |= 1ULL &lt;&lt;(index%64); 肯定可以工作。不需要假设 unsigned long long 是 64 位。
    • @chux unsigned long long 大于 64 位时会崩溃吗?我不确定发生了什么。你能解释一下,没有演员表会发生什么?我看到了错误的输出,并假设它必须对宽度做一些事情(因此成功地尝试了演员表)。我想知道,为什么代码坏了......
    • @Matthias 如果unsigned long long 宽度大于 64 位,它的工作方式相同。
    • @chux 很好的例子。例如,在处理uint64_t 对象时,我更喜欢(uint64_t) 1,所以我确信计算将使用完全相同的类型完成。
    【解决方案4】:

    C为此提供了宏,它将整数常量扩展为int_leastN_t类型。

    INTN_C(value)
    UINTN_C(value)
    

    例子

    #include <stdint.h>.
    bArr[index/64] |= UINT64_C(1) << (index%64);
    

    一般最好避免强制转换。有时会意外地使表达式比预期的更窄。


    UINT64_C 的好处:uint_least_64_t/int_least_64_t 类型必须存在 (C99)。 int64_t/uint64_t 是可选的。

    【讨论】:

    • 不错的宏,感谢您的提示 :) 在这种情况下,转换应该没问题,因为我一直在处理 uin64_t 对吗?
    • 选择INT64_C而不是(int64_t)有什么好处吗?
    • @a3f 答案已编辑以解决一个好处。当 N >= unsigned/int 宽度和 (u)intN_t 存在时,当然 INTN_C() 和强制转换 (intN_t) 行为相似。
    猜你喜欢
    • 1970-01-01
    • 2011-07-01
    • 2011-09-29
    • 2011-11-16
    • 2016-11-27
    • 2021-10-31
    • 1970-01-01
    • 2016-02-21
    相关资源
    最近更新 更多