C++中的位域

4

我有以下代码用于自学:

#include <iostream>
using namespace std;
struct bitfields{
    unsigned field1: 3;
    unsigned field2: 4;
    unsigned int k:  4;
};

int main(){
    bitfields field;
    field.field1=8;
    field.field2=1e7;
    field.k=18;
    cout<<field.k<<endl;
    cout<<field.field1<<endl;
    cout<<field.field2<<endl;
    return 0;
}

我知道 unsigned int k:4 意味着 k 是 4 位宽,最大值为 15,结果如下。
2
0
1

例如,filed1 的取值范围是从0到7(包括0和7),field2k 的取值范围是从0到15。为什么会出现这样的结果?也许应该都是零吗?
3个回答

9

您的字段溢出了。我们以k为例,它有4位宽。它可以存储从0到15的值,在二进制表示中为:

0  -> 0000
1  -> 0001
2  -> 0010
3  -> 0011
...
14 -> 1110
15 -> 1111

因此,当您分配18时,其二进制表示为

18 -> 1 0010 (space added between 4th and 5th bit for clarity)

k 只能保存低四位,因此

k = 0010 = 2.

同样适用于您的其他领域。

4
您看到的这些结果是因为每个位字段都溢出了赋值。
变量filed1是3位,但8需要4位来表示(1000)。低三位都是零,所以filed1为零。
对于filed2,17用10001表示,但filed2只有四位。低四位表示值1。
最后,对于k,18用10010表示,但k只有四位。低四位表示值2。
希望这能帮助你解决问题。

1
在C++中,任何无符号类型在达到其上限时都会循环回绕[1]。当您定义一个4位的位域时,存储的每个值也会被回绕。大小为4的位域的可能值为0-15。如果您存储了“17”,则会回绕到“1”,对于“18”,您会再次前进到“2”。
从数学上讲,回绕值是原始值对目标类型的可能值数量取模:
对于大小为4的位域(2 ** 4个可能值):
18 % 16 == 2
17 % 16 == 1

对于大小为3的位域(有2 ** 3个可能值):

8 % 8 == 0.

[1] 对于有符号类型,情况并非如此,此时发生的情况是未定义的。

1
为什么对于有符号类型会是未定义的情况?例如 int k:4 可以保存 -8 至 7,赋值18仍然得到2,而赋值15则得到-1。我假设这里使用的是二进制补码算术运算,这也是最常见的算术运算方法。其他算术运算方法虽然也有明确定义,但是结果不同。 - falstro
2
C++ 开发的一个目标是尽可能地不依赖底层机器的特性。因此,标准文档对于有符号整数的表示或行为不做任何假设:它们可以是 1 补码数字,并且在溢出时可能会 陷阱 - Nordic Mainframe
1
这当然是很重要的。例如,仍有人编写溢出检查,如 x+1<x,不知道根据规则,这可以替换为“false”。 - Nordic Mainframe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接