如何更改整数的MSB

时间:2016-04-05 00:45:35

标签: c

当我使用下面的代码更改32位,或当bitc等于31时,它给我-2147483643。

似乎将所有0改为1而不是最后0。如何在代码中修复它?在此先感谢您的帮助。

#include <stdio.h>

int main()
{
  int num;
  int newnum;
  int orgnum;
  int bitc;
  int x;
  int check;
  printf("Enter the integer whose bits you want to manipulate. \n");
  scanf("%d",&num);
  orgnum=num;
  printf("What bit would you like to change? 0 is the rightmost bit and 31 is the leftmost bit. \n");
  scanf("%d",&bitc);
  printf("Type in 1 to turn on the bit or 0 to turn of the bit. \n");
  scanf("%d",&x);

  check= (num>>bitc)&1;

  if(x==1)
  {
    newnum=(num |= 1<<bitc);
  }
  else if(x==0)
  {

    if(check==0)
    {
      newnum=num;
    }

    else
    {
      newnum=(num &= ~(1<<x));
    }

  }

  printf("Your orignal number was: ");
  printf("%d", orgnum);
  printf("\n");
  printf("The new number after the bit manipulation is: ");
  printf("%d",newnum);


  //printf("%d //n|=1<<31;
  //printf("%d", n);
  //printf("%d", x);

  return 0;
}

3 个答案:

答案 0 :(得分:1)

首先,正如已经提到的,当你写(1<<x)时,你打算写(1<<bitc)

您遇到的主要问题是您尝试在符号位上使用班次。对于int,最高有效位称为符号位,因为设置时它表示数字为负数。

现在,C在各种各样的系统上运行,历史上CPU已经以不同的方式实现了负数。因此,C标准没有定义将1移入和移出符号位的操作。因此1 << 31会将1转换为符号位,从而导致undefined behaviour

此后还有其他问题,但为了保持简洁,我不会详细说明 - 所有这些问题都由以下建议解决。

为了避免这些符号位问题,最简单和最好的选择是使用无符号类型。将所有num值从int更改为unsigned int,并在所有情况下将1 <<更改为1U <<。然后你可以像任何其他位一样处理最重要的位。

在处理%u时,在scanf和printf中也使用%d代替unsigned int

关于您的代码风格:

假设我们进行此更改以获取newnum=(num |= 1U<<bitc);,它仍然有点复杂。 |=是复合作业,因此您要同时更新numnewnum。但是你只需要更新一个变量。实际上,您有2个冗余变量(orgnumcheck)以及一些冗余测试。

将其与原始代码进行比较:

if(x==1)
    newnum = num | (1U << bitc);
else
    newnum = num & ~(1U << bitc);

您不必在未设置的案例中对0进行预测试,因为它无论如何都会设置0(原因与您{{}未预先测试1相同1}}在设定的情况下)。

同样最好检查用户输入是否在unsigned int的范围内。如果他们为bitc键入负数或大于31的数字,则会再次出现未定义的行为。

如果在具有64位int或16位int的系统上运行代码,该怎么办?基本上你有两个选择:

  • 使用uint32_t代替unsigned int,以保证32位宽度(在这种情况下,再次检查printf说明符,如David C Rankin的回答所示;并使用(uint32_t)1而不是1U
  • 继续使用unsigned int,然后使用表达式CHAR_BIT * sizeof(int) - 1代替31

答案 1 :(得分:0)

我认为您的代码行newnum=(num &= ~(1<<x));不正确,因为您使用x的值将1位移到要更改的位的位置。这一定是造成问题的原因。你不应该使用bitc移位吗?

此外,您必须对C中的int数据类型感到困惑。它是带有二进制补码表示的有符号整数。二进制数补码,其所有位设置为1,将等于十进制数-1,而不是十进制数-2147483643。

提示:二进制数10000000000000000000000000000000(32位),带有二进制补码表示,等于十进制数-2147483648。

答案 2 :(得分:0)

当你开始操作位时,人们陷入的最大陷阱之一就是在签名数据类型上捣乱。由于几个问题,这是有问题的。

例如,当您声明char c = 127;

时会发生什么
c = 01111111;

然后执行c += 1;128无法表示signed char。如果您将c的位视为简短,您会看到:

c = 1111111110000000; (-128 signed, 65408 unsigned)

如果c使用了无符号类型,则符号扩展名不适用。此外,有符号类型的位操作是实现定义(标准的相同部分),这意味着如何处理转换取决于您,C标准未指定。因此,如果您需要明确定义的行为,请将位操作限制为无符号类型。

(您可以自由检查已签名类型的位(例如,确定符号,偶数/奇数等),所有这些都已定义,但是通过位操作更改已签名类型的值是实现定义的 - 通过你)

如果您阅读注释,当您开始摆弄位时,使用精确宽度的变量非常重要。 stdint.h包含确切的宽度类型(例如uint32_t保证32位无符号值)。 inttypes.h提供了可用于使用scanfprintf等读取/打印精确宽度的宏,例如:

uint32_t bit;

printf ("Enter bit to change (0-31): ");
if (scanf ("%" SCNu32 "%*c", &bit) != 1 || bit > 31u) {
    fprintf (stderr, "error: invalid conversion or value for bit.\n");
    return 1;
}

打印精确宽度的工作方式类似:

printf ("bit : %" PRIu32 "\n", bit);

一个非常易读的摘要是Fixed width integer types (since C99)

这个故事的寓意是你在操纵位时需要特别小心,特别是在签名类型上,因为有许多陷阱等待着不知情的人。位操作绝对没有问题,有大量高效的代码可以广泛使用它们,但你必须确保避免与它们相关的微妙问题。 (更不用说现代处理器,编译器,优化,内存大量等等。与它们相关的收益递减)

希望这很有帮助。如果您还有其他问题,请与我们联系。