我在此Quora帖子中看到了以下代码:
#include <stdio.h>
struct mystruct { int enabled:1; };
int main()
{
struct mystruct s;
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabledn"); // --> we think this to be printed
else
printf("Is disabled !!n");
}
在C&amp中;C ,代码的输出是意外,
被禁用!!
尽管在该帖子中给出了"标志"相关的解释,但我无法理解,我们如何设置某些东西,然后它不会像它那样反映。
有人可以给出更详细的解释吗?
NOTE :两个标签C&amp;需要C ,因为它们的标准在描述了位场方面略有不同。请参阅C规格和C 规范的答案。
位字场的定义不佳。给定此代码struct mystruct {int enabled:1;};
,然后我们不知道:
- 这占用多少空间 - 如果有填充位/字节以及它们位于内存中的位置。
- 位置位于内存中的位置。未定义,也取决于endianess。
-
int:n
BITFIELD是否应被视为签名或未签名。
关于最后一部分,C17 6.7.2.1/10说:
一个位场被解释为具有签名或未签名的整数类型,由 指定的位数 125)
非规范说明上述:
125)上述6.7.2中指定,如果使用的实际类型说明符是
int
或定义为int
的Typedef-name,则 然后是实现定义的,无论是签名还是未签名。
如果比特菲尔德被视为 signed int
,并且您的大小1
,则没有数据余地,仅适用于符号。这就是您的程序可能会在某些编译器上给出奇怪结果的原因。
好的练习:
- 永远不要将位字场用于任何目的。
- 避免对任何形式的位操作使用签名的
int
类型。
我无法理解,我们如何设置某些东西,然后它不会像它那样出现。
您是在问为什么它编译与给您错误?
是的,理想情况下应该给您一个错误。如果您使用编译器的警告,确实如此。在GCC中,带有-Werror -Wall -pedantic
:
main.cpp: In function 'int main()':
main.cpp:7:15: error: overflow in conversion from 'int' to 'signed char:1'
changes value from '1' to '-1' [-Werror=overflow]
s.enabled = 1;
^
为什么要实施定义的原因与错误可能与历史用法有关,在这种情况下,需要演员会破坏旧代码。该标准的作者可能认为警告足以为有关的人带来懈怠。
要投入一些处方主义,我会回应 @lundin的语句:"永远不要为任何目的使用刻度。关于您的内存布局详细信息,这将使您首先认为需要比特菲尔德,几乎可以肯定的是,您的其他相关要求将与他们的规定相比。
(tl; dr--如果您足够复杂以合法地"需要"位景点,则它们的定义不足以为您服务。)
这是实现定义的行为。我假设您正在使用Twos-Compliment签名的整数并在这种情况下将int
视为签名整数,以解释为什么如果IF语句的真实部分不输入。
struct mystruct { int enabled:1; };
将enable
声明为1位景点。由于已签名,有效值为-1
和0
。将字段设置为1
溢出,然后将其重新回到-1
(这是未定义的行为)
本质上是在处理签名的位点时最大值为 2^(bits - 1) - 1
,在这种情况下为 0
。在2的补充系统中,最左侧的位是符号位。因此,任何具有最左侧位的签名整数都是负值。
如果您有一个1位签名的整数,则只有符号位。因此,将1
分配给该位只能设置符号位。因此,当回读后,该值被解释为负面,是-1。
值1位签名的整数可以保存的值是-2^(n-1)= -2^(1-1)= -2^0= -1
和2^n-1= 2^1-1=0
根据C 标准N4713,提供了非常相似的代码片段。使用的类型是BOOL
(自定义),但可以应用于任何类型。
12.2.4
4 如果将值true或false存储到任何大小的
bool
的位置(包括一个位点),则原始bool
比特场的值和值应相等。如果将枚举者的值存储在相同的枚举类型的位,并且位列中的位数足够大,可以保持 该枚举类型(10.2)的所有值,原始枚举者的值和位字段的值 应比较相等的。 [示例:enum BOOL { FALSE=0, TRUE=1 }; struct A { BOOL b:1; }; A a; void f() { a.b = TRUE; if (a.b == TRUE) // yields true { /* ... */ } }
- 结束示例]
在第一眼,大胆的部分似乎是为了解释而开放的。但是,当enum BOOL
从int
派生时,正确的意图将变得明确。
enum BOOL : int { FALSE=0, TRUE=1 }; // ***this line
struct mystruct { BOOL enabled:1; };
int main()
{
struct mystruct s;
s.enabled = TRUE;
if(s.enabled == TRUE)
printf("Is enabledn"); // --> we think this to be printed
else
printf("Is disabled !!n");
}
使用上述代码,它在没有-Wall -pedantic
的情况下发出警告:
警告:" mystruct ::启用"太小,无法保持所有"枚举bool"的值
struct mystruct { BOOL enabled:1; };
输出为:
被禁用!!(使用
enum BOOL : int
时)
如果使enum BOOL : int
简单enum BOOL
,则输出为上述标准PASAGE指定:
已启用(使用
enum BOOL
时)
因此,可以得出结论,也可以像其他答案一样, int
类型不够大,不足以将值存储在一个位点中。
我对比特菲尔德的理解没有错。我看到的是,您首先重新定义了MyStruct为 struct mystruct {int eneabled:1;} ,然后用作 struct mystruct s; 。您应该编码的是:
#include <stdio.h>
struct mystruct { int enabled:1; };
int main()
{
mystruct s; <-- Get rid of "struct" type declaration
s.enabled = 1;
if(s.enabled == 1)
printf("Is enabledn"); // --> we think this to be printed
else
printf("Is disabled !!n");
}