从草案C ++标准(N3337):
9.6位字段
4如果值
true
或false
存储在任何大小的bool
类型的位字段中(包括一位位字段),则原始{{1值和比特字段的值应相等。如果枚举数的值存储在相同枚举类型的位字段中,并且位字段中的位数足以容纳该枚举类型的所有值(7.2),则原始枚举器值和比特字段的值应比较相等。
该标准对于其他类型的位字段的任何此类行为都是非约定的。为了理解g ++(4.7.3)如何处理其他类型的位域,我使用了以下测试程序:
bool
输出:
d1: true d2: 1 d3: -1 d4: 1 true true false true
我对与#include <iostream>
enum TestEnum
{
V1 = 0,
V2
};
struct Foo
{
bool d1:1;
TestEnum d2:1;
int d3:1;
unsigned int d4:1;
};
int main()
{
Foo foo;
foo.d1 = true;
foo.d2 = V2;
foo.d3 = 1;
foo.d4 = 1;
std::cout << std::boolalpha;
std::cout << "d1: " << foo.d1 << std::endl;
std::cout << "d2: " << foo.d2 << std::endl;
std::cout << "d3: " << foo.d3 << std::endl;
std::cout << "d4: " << foo.d4 << std::endl;
std::cout << std::endl;
std::cout << (foo.d1 == true) << std::endl;
std::cout << (foo.d2 == V2) << std::endl;
std::cout << (foo.d3 == 1) << std::endl;
std::cout << (foo.d4 == 1) << std::endl;
return 0;
}
对应的输出行感到惊讶。 ideone.com处的输出相同。
由于标准对于Foo::d3
类型的位字段的比较是非约束的,因此g ++似乎没有违反标准。这让我想到了我的问题。
使用int
类型的位字段是个坏主意吗?是否应该气馁?
答案 0 :(得分:4)
是的,int
类型的位字段是个坏主意,因为它们的签名是实现定义的。请改用signed int
或unsigned int
。
对于非位域声明,类型名称int
完全等同于signed int
(或int signed
或signed
)。 short
,long
和long long
遵循相同的模式:未加修饰的类型名称是已签名的版本,您必须添加{{1 }}关键字命名相应的无符号类型。
由于历史原因,位字段是一种特殊情况。使用unsigned
类型定义的位字段与使用int
的相同声明或相同,或与signed int
相同的声明。选择是实现定义的(即,它取决于编译器,而不是程序员)。位字段是unsigned int
和int
不是(必然)同义的唯一上下文。这同样适用于signed int
,char
,short
和long
。
引用C ++ 11标准,第9.6节[class.bit]:
它是实现定义的是否是普通的(既不是明确的 签名或未签名)
long long
,char
,short
, {{1} } ,或int
位字段 签名或未签名。
(我不完全确定这个的基本原理。非常旧版本的C没有long
关键字,无符号位字段通常比有符号位字段更有用。可能是一些早期的C编译器在引入long long
关键字之前实现了位字段。默认情况下将位字段设置为无符号,即使声明为unsigned
,也可能只是为了方便。没有真正的理由保留规则,以避免破坏旧代码。)
大多数位字段都是无符号的,这当然意味着它们应该以这种方式定义。
如果你想要一个签名的位字段(比如说,一个4位字段可以表示-8到+7之间的值,或者非二进制补码中的-7到+7之间的值)系统),然后你应该明确地将其定义为unsigned
。如果您将其定义为int
,则某些编译器会将其视为signed int
。
如果您不关心您的位字段是有符号还是无符号,那么您可以将其定义为int
- 但如果您要定义位字段,那么您几乎可以肯定做< / em>关心它是签名还是未签名。
答案 1 :(得分:2)
您绝对可以使用任何大小不超过unsigned
大小的unsigned int
位字段。虽然signed
位字段是合法的(至少如果宽度大于1),我个人不希望使用它们。但是,如果您确实想要使用带符号的位字段,则应将其明确标记为signed
,因为它与实现有关,是指非int
位字段是有符号还是无符号。 (这类似于char
,但没有明确不合格的char*
文字的复杂特征。)
所以在这个程度上,我同意不鼓励int
位字段。 [注1]虽然我不知道任何int
位域是隐式无符号的实现,但标准肯定允许这样做,因此如果实现特定的意外行为有很多机会,你没有明确的迹象。
标准规定有符号整数表示由可选的填充位,正好一个符号位和值位组成。虽然标准不保证至少有一个值位, - 并且如OP中所示,gcc
并不坚持存在 - 我认为这是对标准的合理解释,因为它明确允许没有填充位,并且没有任何与值位相对应的措辞。
在任何情况下,只允许三种可能的签名表示:
2&#39的补码,其中由1
组成的单比特字段应解释为-1
1&#39的补码和符号幅度。在这两种情况下,允许由1
组成的单比特字段作为陷阱表示,因此可以在1比特有符号比特字段中表示的唯一数字是0
由于便携式代码不能假设1位有符号位字段可以表示任何非零值,因此无论您是否解释标准,坚持有符号位字段至少有2位似乎是合理的( s)实际上要求与否。
注意:
unsigned char
。但是在那一点上没有办法回滚历史。)答案 2 :(得分:0)
int
为signed
,并且在C ++ Two's complement中可以使用,因此在第一个int
中可以存储字节符号。当signed int
有2位时,它可以等于1,see it working。
答案 3 :(得分:0)
这是完全合乎逻辑的。 int
是有符号整数类型,如果底层架构使用两个补码来表示有符号整数(如所有现代架构那样),则高位是符号位。因此,1位有符号整数位域可以取值0
或-1
。例如,3位有符号整数位域可以取-4
和3
之间的值。
只要您理解了两个补码表示,就没有理由对有符号整数位域进行全面禁止。