【问题标题】:How to use signed or unsigned integer for bit mapping in C?如何在 C 中使用有符号或无符号整数进行位映射?
【发布时间】:2021-01-26 12:22:17
【问题描述】:

我想重置 int32_t 的第 31 位(最后一位,0 到 31 范围),只有这种情况似乎失败了。 即,当“i”为 31 时,输出失败,它返回 -1。错误是什么?如何解决?

#include <stdio.h>
#include <stdlib.h>

void Release(int ResetBit, int32_t *Val)
{
    int32_t testBit = 1; /* XoR Bit */

    if (ResetBit >= 0 && ResetBit < 32)
    {
        testBit = (testBit << ResetBit);
        *Val ^= testBit;
    }
    else
    {
        perror("Max range is 0-31 only, failed! ");
        //exit(1);
    }  
}

int main(int argc, char const *argv[])
{
    int count = 0;
    for (int i = 0; i < 32; i++)
    {
        int32_t MaxValue = 2147483647;
        Release(i, &MaxValue);
        printf("MaxValue = %d NodeID = % d\n", MaxValue, i);
        count++;
   }
    printf("%d", count);
    return 0;
}

情况 i = 31 的输出是:

MaxValue = -1 节点 ID = 31

【问题讨论】:

  • 好吧,不要为此使用有符号整数(移位有符号整数可能会导致未定义的行为)。使用无符号,即uint32_t。除此之外,您还不清楚Release 是什么意思——它是“设置为零”还是“切换位”?
  • 我想“设置为零”一个特定的位(在 0-31 范围内)。要将特定位设置为零,我将“i”作为位位置传递。例如:将第 5 位设置为零,i = 4。
  • @4386427 我尝试使用 u_int32_t,它给出了同样的错误
  • 结果没有任何问题,它是您的所有位设置的签名值。如果你想看到未签名的使用unsigned打印%u

标签: c memory memory-management bitmap out-of-memory


【解决方案1】:

使用正确的按位运算。重置位使用&amp;

int32_t ResetBit(int bit, int32_t *val)
{
    uint32_t mask = ~(1UL << bit);

    *val &= mask;
    return *val;
}

及用法:

void printnitd(int32_t val)
{
    for(uint32_t mask = 1UL << 31; mask; mask >>= 1)
    {
        printf("%c",  (val & mask) ? '1' : '0');
    }
}

int main(void)
{
    for(int bit = 0; bit < 32; bit++)
    {
        int32_t a = -1;
        printf("bit %u = ", a);
        printnitd(ResetBit(bit, &a));
        printf("\n");
    }
}

【讨论】:

    【解决方案2】:

    如果您不需要实际的签名类型,请使用uint32_t,所有问题都会消失。在有符号类型上使用位运算符的问题是各种形式的定义不明确的行为。

    例如,将某些内容左移到 int32_t 的符号位会导致未定义的行为,这意味着如果您的编译器未使用非标准扩展覆盖这种情况,则会出现潜在错误。类似地,负数右移可以导致算术或逻辑移位,C 标准没有指定哪一种,但允许两种形式。

    话虽如此,如果您只是想设置/清除int32_t 的第 31 位,则可以这样进行:

    int32_t i32 = ...;
    i32 |= 1u << 31;    // set MSB
    i32 &= ~(1u << 31); // clear MSB
    i32 ^= 1u << 31;    // toggle MSB
    

    u 确保无符号算术。

    【讨论】:

    • @4386427 为什么会这样?那篇文章是关于隐式提升/转换的不同主题。
    • 这里不是有无符号到有符号的转换吗?这不属于链接答案最后一部分的#3吗?
    • @4386427 是的,有一个从有符号到无符号的左值转换,它是由实现定义的——主要是因为 C 允许奇异的符号格式。在实践中,impl.-defined 部分是“这台计算机使用 2 的补码”,但在我曾经使用过的每个编译器上的行为都是确定性的。这意味着无符号数被转换为 2 的补码中的原始二进制等价物。
    【解决方案3】:

    首先:位图不要使用有符号整数。始终使用无符号。原因是对有符号整数进行位移可能会导致未定义的行为,而对无符号整数进行位移总是安全的。

    其次:您在 Release 函数中使用 XOR。与testBit 进行异或不会清除一点。 XOR 将切换位值,即 1 变为 0,0 变为 1。相反,您想要:*Val &amp;= ~testBit; 它的工作方式如下:

    If testBit is    0000.0000.0000.0000.0000.0000.0000.1000
    then ~testbit is 1111.1111.1111.1111.1111.1111.1111.0111
    then *Val &= ... will clear bit number 3 and keep all other unchanged 
    as `&` is a bitwise AND operation.
    

    使用无符号时,请记住将printf 更改为打印无符号而不是使用%d,即像printf("%" PRIu32 "\n", uint32t_variable);

    编辑

    XOR 出了什么问题?

    假设您正在使用uint32_t 和 XOR,那么会发生这种情况:

    您的输入是

    0111.1111.1111.1111.1111.1111.1111.1111
    

    和你异或

    1000.0000.0000.0000.0000.0000.0000.0000
    

    切换位 31 导致

    1111.1111.1111.1111.1111.1111.1111.1111
    

    该函数应该清除第 31 位,但它没有。 XOR 不是正确的运算符。

    【讨论】:

    • 即使异或切换,是不是达到了要求的任务,即*Var ^= testBit; ?你能详细说明一下吗?
    • Don't use signed integers for bitmaps. 在这种情况下实际上并不重要。面具很重要
    • @P__J__ 来自 N1570 6.3.1.3 " ... 新类型已签名,无法在其中表示值;结果是实现定义的,或者引发了实现定义的信号。"
    • 这里不适用
    • @P__J__ 嗯...那么适用于什么?
    猜你喜欢
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-11
    • 2013-09-01
    • 2015-01-24
    相关资源
    最近更新 更多