c位操作错误

时间:2016-03-22 06:29:01

标签: c macos gcc bit

更新了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

操作系统是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

为什么?

2 个答案:

答案 0 :(得分:7)

首先,负整数的右移是实现定义的:

  

E1 >> E2的结果是   E1右移E2位位置。如果E1具有无符号类型或E1具有有符号类型和非负值,则结果的值为积分   E1 / 2^E2的商的一部分。如果E1具有有符号类型和负值,则结果值是实现定义的。

但是,让我们假设我们使用的是x86,并且负整数的右移就像是2的补码表示一样,并产生正确的32位值。如果int x = 0xFFFFFFFFx & mask10x55555555(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 < / strong>整数,因为它们永远不会溢出,如果你溢出它们,你的&#34; 32位&#34; int变量甚至可以包含 64位值

答案 1 :(得分:1)

我准备去睡觉,但后来我对这个答案感到困扰,所以我起床了。我不是关于初始化/不初始化的事情。很难知道,因为你的代码只有1/2,但我认为它更多的是签名/无符号差异。

你正在向右移动,对于带符号的数字保留负位;但对于未签名的不会。看起来您的数字集具有较小的值是在轮班中使用无符号数字的结果。

我注意到您没有指定x的类型。这对于C来说真的很奇怪,你真的应该把一个类型放在那个变量的前面。

---原帖如下---

x = 0xffffffff;

您清楚地将变量x初始化为已知的特定值。

中的

if (x != 0xffffffff) {

根据已知的特定值,清楚地检查未初始化变量x中的内容。简而言之,在程序运行之前,您根本不知道变量x的内存是什么,这就是您要阅读的内容。

你只是“幸运”并且有一些“看起来正确”的输出,但是如果你以不同的方式运行它,或者在不同的机器上运行它,你可能根本不会获得相同的输出。