当我使用下面的代码更改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;
}
答案 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);
,它仍然有点复杂。 |=
是复合作业,因此您要同时更新num
和newnum
。但是你只需要更新一个变量。实际上,您有2个冗余变量(orgnum
和check
)以及一些冗余测试。
将其与原始代码进行比较:
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
提供了可用于使用scanf
,printf
等读取/打印精确宽度的宏,例如:
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)。
这个故事的寓意是你在操纵位时需要特别小心,特别是在签名类型上,因为有许多陷阱等待着不知情的人。位操作绝对没有问题,有大量高效的代码可以广泛使用它们,但你必须确保避免与它们相关的微妙问题。 (更不用说现代处理器,编译器,优化,内存大量等等。与它们相关的收益递减)
希望这很有帮助。如果您还有其他问题,请与我们联系。