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

C++CBit FieldsSigned IntegerImplementation Defined-Behavior

C++ Problem Overview


I saw the below code in this Quora post:

#include <stdio.h>
 
struct mystruct { int enabled:1; };
int main()
{
  struct mystruct s;
  s.enabled = 1;
  if(s.enabled == 1)
    printf("Is enabled\n"); // --> we think this to be printed
  else
    printf("Is disabled !!\n");
}

In both C & C++, the output of the code is unexpected,

> Is disabled !!

Though the "sign bit" related explanation is given in that post, I am unable to understand, how it is possible that we set something and then it doesn't reflect as it is.

Can someone give a more elaborate explanation?


Note: Both the tags [tag:c] & [tag:c++] are required, because their standards slightly differ for describing the bit-fields. See answers for C specification and C++ specification.

C++ Solutions


Solution 1 - C++

Bit-fields are incredibly poorly defined by the standard. Given this code struct mystruct {int enabled:1;};, then we don't know:

  • How much space this occupies - if there are padding bits/bytes and where they are located in memory.
  • Where the bit is located in memory. Not defined and also depends on endianess.
  • Whether an int:n bitfield is to be regarded as signed or unsigned.

Regarding the last part, C17 6.7.2.1/10 says:

> A bit-field is interpreted as having a signed or unsigned integer type consisting of the specified number of bits 125)

Non-normative note explaining the above:

> 125) As specified in 6.7.2 above, if the actual type specifier used is int or a typedef-name defined as int, then it is implementation-defined whether the bit-field is signed or unsigned.

In case the bitfield is to be regarded as signed int and you make a bit of size 1, then there is no room for data, only for the sign bit. This is the reason why your program might give weird results on some compilers.

Good practice:

  • Never use bit-fields for any purpose.
  • Avoid using signed int type for any form of bit manipulation.

Solution 2 - C++

> I am unable to understand, how is it possible that we set something and then it doesn't show up as it is.

Are you asking why it compiles vs. gives you an error?

Yes, it should ideally give you an error. And it does, if you use your compiler's warnings. In GCC, with -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;
           ^

The reasoning for why this is left up to being implementation-defined vs. an error may have more to do with historical usages, where requiring a cast would mean breaking old code. The authors of the standard may believe warnings were enough to pick up the slack for those concerned.

To throw in some prescriptivism, I'll echo @Lundin's statement: "Never use bit-fields for any purpose." If you have the kind of good reasons to get low-level and specific about your memory layout details that would get you to thinking you needed bitfields in the first place, the other associated requirements you almost certainly have will run up against their underspecification.

(TL;DR - If you're sophisticated enough to legitimately "need" bit-fields, they're not well-defined enough to serve you.)

Solution 3 - C++

This is implementation defined behavior. I am making the assumption that the machines you are running this on use twos-compliment signed integers and treat int in this case as a signed integer to explain why you don't enter if true part of the if statement.

struct mystruct { int enabled:1; };

declares enable as a 1 bit bit-field. Since it is signed, the valid values are -1 and 0. Setting the field to 1 overflows that bit going back to -1 (this is undefined behavior)

Essentially when dealing with a signed bit-field the max value is 2^(bits - 1) - 1 which is 0 in this case.

Solution 4 - C++

You could think of it as that in the 2's complement system, the left-most bit is the sign bit. Any signed integer with the left-most bit set is thus a negative value.

If you have a 1-bit signed integer, it has only the sign bit. So assigning 1 to that single bit can only set the sign bit. So, when reading it back, the value is interpreted as negative and so is -1.

The values a 1 bit signed integer can hold is -2^(n-1)= -2^(1-1)= -2^0= -1 and 2^n-1= 2^1-1=0

Solution 5 - C++

As per the C++ standard n4713, a very similar code snippet is provided. The type used is BOOL (custom), but it can apply to any type.

> ###12.2.4

> 4 If the value true or false is stored into a bit-field of type bool of any size (including a one bit bit-field), the original bool value and the value of the bit-field shall compare equal. If the value of an enumerator is stored into a bit-field of the same enumeration type and the number of bits in the bit-field is large enough to hold all the values of that enumeration type (10.2), the original enumerator value and the value of the bit-field shall compare equal. [ Example:

> enum BOOL { FALSE=0, TRUE=1 }; struct A { BOOL b:1; }; A a; void f() { a.b = TRUE; if (a.b == TRUE) // yields true { /* ... */ } }

> — end example ]


At 1st glance, the bold part appears open for interpretation. However, the correct intent becomes clear when the enum BOOL is derived from the 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 enabled\n"); // --> we think this to be printed
  else
    printf("Is disabled !!\n");
}

With above code it gives a warning without -Wall -pedantic:

> warning: ‘mystruct::enabled’ is too small to hold all values of ‘enum BOOL’ struct mystruct { BOOL enabled:1; };

The output is:

> Is disabled !! (when using enum BOOL : int)

If enum BOOL : int is made simple enum BOOL, then the output is as the above standard pasage specifies:

> Is enabled (when using enum BOOL)


Hence, it can be concluded, also as few other answers have, that int type is not big enough to store value "1" in just a single bit bit-field.

Solution 6 - C++

There is nothing wrong with your understanding of bitfields that I can see. What I see is that you redefined mystruct first as struct mystruct { int enabled:1; } and then as struct mystruct s;. What you should have coded was:

#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 enabled\n"); // --> we think this to be printed
    else
        printf("Is disabled !!\n");
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioniammilindView Question on Stackoverflow
Solution 1 - C++LundinView Answer on Stackoverflow
Solution 2 - C++HostileFork says dont trust SEView Answer on Stackoverflow
Solution 3 - C++NathanOliverView Answer on Stackoverflow
Solution 4 - C++Paul OgilvieView Answer on Stackoverflow
Solution 5 - C++iammilindView Answer on Stackoverflow
Solution 6 - C++ar18View Answer on Stackoverflow