c位运算错误
c bit operation bug
x 的来源已更新。这是一项任务,我被要求填写该功能。作业的要求是我不能更改参数类型或进行任何类型转换。所以我有这个问题。
我想使用 c 实现位计数。这是代码:
int bitCount(int x) {
int mask1 = 0x55555555;
int mask2 = 0x33333333;
int mask3 = 0x0f0f0f0f;
int mask4 = 0x00ff00ff;
int mask5 = 0x0000ffff;
//x = 0xffffffff;
if (x != 0xffffffff) {
exit(0);
}
printf("%x\n", x);
x = (x & mask1) + ((x >> 1) & mask1);
printf("%x\n", x);
x = (x & mask2) + ((x >> 2) & mask2);
printf("%x\n", x);
x = (x & mask3) + ((x >> 4) & mask3);
printf("%x\n", x);
x = (x & mask4) + ((x >> 8) & mask4);
printf("%x\n", x);
x = (x & mask5) + ((x >> 16) & mask5);
printf("%x\n", x);
return x;
}
当x
= -1(或十六进制的0xffffffff
)时,答案应该是0x20
。但实际上输出是:
ffffffff
aaaaaaaa
24444444
6080808
e0010
1e
如果我取消注释该行
"x = 0xffffffff"
在代码中,输出变为:
ffffffff
aaaaaaaa
44444444
8080808
100010
20
OS 是 Mac OS X.gcc 版本是:
gcc --version
配置为:
--prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin14.5.0
Thread model: posix
为什么?
我正要睡觉,但后来被这个答案打扰了,所以我起床了。我不认为这是关于初始化/不初始化。很难知道,因为你的代码只有 1/2,但我认为更多的是关于有符号/无符号的差异。
您正在右移,对于有符号数,它会保留负数位;但对于未签名的不会。看起来您的数字集具有较小的值是在您的班次中使用无符号数字的结果。
我注意到您没有指定 x
的类型。这对 C
来说真的很奇怪,你真的应该在该变量前面加上一个类型。
---原post如下---
在
x = 0xffffffff;
您显然将变量 x
初始化为已知的特定值。
在
if (x != 0xffffffff) {
您清楚地根据已知的特定值检查未初始化变量 x
中的任何内容。简而言之,您不知道程序 运行 之前变量 x
的 ram 中有什么内容,而这正是您将要阅读的内容。
你只是 "got lucky" 并且有一些输出 "sort of looked right",但是如果你 运行 以不同的方式或在不同的机器上,你很可能不会得到完全相同的输出。
首先,负整数的右移是实现定义的:
The result of E1 >> E2
is
E1
right-shifted E2
bit positions. If E1
has an unsigned type or if E1
has a signed type and a nonnegative value, the value of the result is the integral
part of the quotient of E1 / 2^E2
. If E1 has a signed type and a negative value, the resulting value is implementation-defined.
但是,假设我们使用的是 x86,负整数的右移就像 2 的补码表示一样,并产生正确的 32 位值。如果int x = 0xFFFFFFFF
那么x & mask1
就是0x55555555
并且(x >> 1) & mask
也是0x55555555
,都是正数;他们的总和是0xAAAAAAAA
然后
x = (x & mask1) + ((x >> 1) & mask1);
如果 int
是 32 位宽, 会导致 有符号整数溢出 ,因此您的代码具有 未定义的行为 。你应该必须使用unsigned int
而不是int
。
我无法在我的计算机上重现您的错误,但我可以通过在溢出加法后屏蔽 x
中的符号位来获得相同的输出:
x = (x & mask1) + ((x >> 1) & mask1);
x = x & 0x7FFFFFFF;
只有通过此更改,我才能获得您所观察到的错误输出:
ffffffff
aaaaaaaa
24444444
6080808
e0010
1e
当 64 位处理器变得越来越普遍时,32 位整数的这种未定义行为增加了很多 - 根据 C 标准,允许编译器将 64 位寄存器用于 32 位 signed 整数,因为 它们永远不会溢出 如果你溢出它们,你的“32 位” int
变量甚至可以包含一个 64位值.
x 的来源已更新。这是一项任务,我被要求填写该功能。作业的要求是我不能更改参数类型或进行任何类型转换。所以我有这个问题。
我想使用 c 实现位计数。这是代码:
int bitCount(int x) {
int mask1 = 0x55555555;
int mask2 = 0x33333333;
int mask3 = 0x0f0f0f0f;
int mask4 = 0x00ff00ff;
int mask5 = 0x0000ffff;
//x = 0xffffffff;
if (x != 0xffffffff) {
exit(0);
}
printf("%x\n", x);
x = (x & mask1) + ((x >> 1) & mask1);
printf("%x\n", x);
x = (x & mask2) + ((x >> 2) & mask2);
printf("%x\n", x);
x = (x & mask3) + ((x >> 4) & mask3);
printf("%x\n", x);
x = (x & mask4) + ((x >> 8) & mask4);
printf("%x\n", x);
x = (x & mask5) + ((x >> 16) & mask5);
printf("%x\n", x);
return x;
}
当x
= -1(或十六进制的0xffffffff
)时,答案应该是0x20
。但实际上输出是:
ffffffff
aaaaaaaa
24444444
6080808
e0010
1e
如果我取消注释该行
"x = 0xffffffff"
在代码中,输出变为:
ffffffff
aaaaaaaa
44444444
8080808
100010
20
OS 是 Mac OS X.gcc 版本是:
gcc --version
配置为:
--prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin14.5.0
Thread model: posix
为什么?
我正要睡觉,但后来被这个答案打扰了,所以我起床了。我不认为这是关于初始化/不初始化。很难知道,因为你的代码只有 1/2,但我认为更多的是关于有符号/无符号的差异。
您正在右移,对于有符号数,它会保留负数位;但对于未签名的不会。看起来您的数字集具有较小的值是在您的班次中使用无符号数字的结果。
我注意到您没有指定 x
的类型。这对 C
来说真的很奇怪,你真的应该在该变量前面加上一个类型。
---原post如下---
在
x = 0xffffffff;
您显然将变量 x
初始化为已知的特定值。
在
if (x != 0xffffffff) {
您清楚地根据已知的特定值检查未初始化变量 x
中的任何内容。简而言之,您不知道程序 运行 之前变量 x
的 ram 中有什么内容,而这正是您将要阅读的内容。
你只是 "got lucky" 并且有一些输出 "sort of looked right",但是如果你 运行 以不同的方式或在不同的机器上,你很可能不会得到完全相同的输出。
首先,负整数的右移是实现定义的:
The result of
E1 >> E2
isE1
right-shiftedE2
bit positions. IfE1
has an unsigned type or ifE1
has a signed type and a nonnegative value, the value of the result is the integral part of the quotient ofE1 / 2^E2
. If E1 has a signed type and a negative value, the resulting value is implementation-defined.
但是,假设我们使用的是 x86,负整数的右移就像 2 的补码表示一样,并产生正确的 32 位值。如果int x = 0xFFFFFFFF
那么x & mask1
就是0x55555555
并且(x >> 1) & mask
也是0x55555555
,都是正数;他们的总和是0xAAAAAAAA
然后
x = (x & mask1) + ((x >> 1) & mask1);
如果 int
是 32 位宽, 会导致 有符号整数溢出 ,因此您的代码具有 未定义的行为 。你应该必须使用unsigned int
而不是int
。
我无法在我的计算机上重现您的错误,但我可以通过在溢出加法后屏蔽 x
中的符号位来获得相同的输出:
x = (x & mask1) + ((x >> 1) & mask1);
x = x & 0x7FFFFFFF;
只有通过此更改,我才能获得您所观察到的错误输出:
ffffffff
aaaaaaaa
24444444
6080808
e0010
1e
当 64 位处理器变得越来越普遍时,32 位整数的这种未定义行为增加了很多 - 根据 C 标准,允许编译器将 64 位寄存器用于 32 位 signed 整数,因为 它们永远不会溢出 如果你溢出它们,你的“32 位” int
变量甚至可以包含一个 64位值.