为什么将值分配给一个位字段不给予相同的值

Why is assigning a value to a bit field not giving the same value back?

本文关键字:字段 不给予 一个 分配 为什么      更新时间:2023-10-16

我在此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位景点。由于已签名,有效值为-10。将字段设置为1溢出,然后将其重新回到-1(这是未定义的行为)

本质上是在处理签名的位点时最大值为 2^(bits - 1) - 1,在这种情况下为 0。在2的补充系统中,最左侧的位是符号位。因此,任何具有最左侧位的签名整数都是负值。

如果您有一个1位签名的整数,则只有符号位。因此,将1分配给该位只能设置符号位。因此,当回读后,该值被解释为负面,是-1。

值1位签名的整数可以保存的值是-2^(n-1)= -2^(1-1)= -2^0= -12^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 BOOLint派生时,正确的意图将变得明确。

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");
}