65

I'm working on a microcontroller with only 2KB of SRAM and desperately need to conserve some memory. Trying to work out how I can put 8 0/1 values into a single byte using a bitfield but can't quite work it out.

struct Bits
{
    int8_t b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
};

int main(){
    Bits b;
    b.b0 = 0;
    b.b1 = 1;

    cout << (int)b.b0; // outputs 0, correct
    cout << (int)b.b1; // outputs -1, should be outputting 1
}

What gives?

Community
  • 1
  • 1
Jazcash
  • 3,145
  • 2
  • 31
  • 46
  • 13
    Have you looked at using a [`std::bitset`](http://en.cppreference.com/w/cpp/utility/bitset) – NathanOliver Apr 24 '15 at 19:18
  • 10
    You may use unsigned instead of int –  Apr 24 '15 at 19:20
  • @1nflktd the answer is the same for both in this case, though NathanOliver's suggestion of using a bitset might be a better choice in the C++ world. – Carl Norum Apr 24 '15 at 19:22
  • 1
    I haven't, but the standard library isn't available to me on the platform I'm working on (AVR/Atmel), so it's not an option unfortunately. – Jazcash Apr 24 '15 at 19:25
  • 25
    Actually, throwing C++ away and sticking to C will cut your memory use down significantly. Absent that, your code is fine. -1 is "true". You can also, as Dieter suggests, make your fields unsigned. – Lee Daniel Crocker Apr 24 '15 at 19:26
  • I'm in too deep at this point with C++ using operator overloading, default method parameters and one or two other things to be able to port down to C. – Jazcash Apr 24 '15 at 19:29
  • 3
    @LeeDanielCrocker, using `b.b1` by itself will evaluate true, but watch out for `b.b1 == true`. Ask me how I know.... – Carl Norum Apr 24 '15 at 19:30
  • 2
    Agreed. No C programmer would ever write `== TRUE`. – Lee Daniel Crocker Apr 24 '15 at 19:32
  • 1
    Unfortunately, we all sometimes have to work with chuckleheads. :-) – Carl Norum Apr 24 '15 at 19:33
  • 2
    So basically this question should *not* have been tagged "C", since you must use C++? – Jongware Apr 24 '15 at 19:40
  • 3
    the definition of the bit fields should use 'unsigned int8, not int8 otherwise each bit will try to be the sign bit., which results in difficulties. – user3629249 Apr 24 '15 at 19:53
  • 1
    There is always the alternative of bitshifting manually. `(b >> i) & 1` to get the i-th bit. That way you can access them via a variable index. – CodesInChaos Apr 25 '15 at 09:36

4 Answers4

113

All of your bitfield members are signed 1-bit integers. On a two's complement system, that means they can represent only either 0 or -1. Use uint8_t if you want 0 and 1:

struct Bits
{
    uint8_t b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
};
Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • NMDV, yet on another 2's complement system, might the values of `int8_t b0:1` be `0` and `1`? I find scant info on signed 1 bit width bit-fields. The only thing I find in the C/C++ specs about `int8_t` with bit-fields is that it is an "implementation-defined" type. – chux - Reinstate Monica Apr 24 '15 at 20:34
  • 9
    @chux If the representable values were *not* 0 and -1 it would not be a 2s-complement representation. (In the hypothetical case of a 1s-complement or sign-magnitude representation, the representable values would be +0 and -0, or possibly 0 and [trap representation]. I'm not aware of *any* signed integer format in which a 1-bit field would represent the values 0 and 1.) – zwol Apr 24 '15 at 20:55
  • 4
    @zwol Agreed: C specifies 'the sign bit has the value −(2^M) (two’s complement);" §6.2.6.2 2. So that specifies it must be `0` or `-1`. – chux - Reinstate Monica Apr 24 '15 at 20:57
  • 1
    @chux Pedantically, a two's complement system could define `int8_t` to be `char` and then define `char` bitfields to be unsigned, so the program is still implementation-dependent. In fact, the common ABI specifies the latter. The former is up to the library implementation and is fairly arbitrary when `char` is signed. – Potatoswatter Apr 25 '15 at 12:42
  • Just a caution on use of single bits. If the underlying compiled code uses read-modify-write of the entire byte to set a bit, you may find interrupt handlers that use similar operations on these bits can cause corruption of the data. – JohntheFish Apr 29 '15 at 07:58
14

As a word of caution - the standard doesn't really enforce an implementation scheme for bitfields. There is no guarantee that Bits will be 1 byte, and hypothetically it is entirely possible for it to be larger.

In practice however the actual implementations usually follow the obvious logic and it will "almost always" be 1 byte in size, but again, there is no requirement that it is guaranteed. Just in case you want to be sure, you could do it manually.

BTW -1 is still true but it -1 != true

Community
  • 1
  • 1
dtech
  • 47,916
  • 17
  • 112
  • 190
  • Can You put a reference? Iirc the size is guaranteed the order isnt – Alexander Oh Apr 24 '15 at 22:44
  • @Alex - I don't seem to find anything about guaranteeing the size. – dtech Apr 24 '15 at 22:59
  • I think the size is safe (but the implementation doesn't have to support anything other than `int` and `unsigned int` I think): "An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit." I think that means if the implementation allows an 8-bit type for bitfields that OP's code is fine. – Carl Norum Apr 24 '15 at 23:15
3

As noted, these variables consist of only a sign bit, so the only available values are 0 and -1.

A more appropriate type for these bitfields would be bool. C++14 §9.6/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.

Yes, std::uint8_t will do the job, but you might as well use the best fit. You won't need things like the cast for std::cout << (int)b.b0;.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • 2
    `bool` is not required to be 1 byte, `std::uint8_t` is. – edmz Apr 25 '15 at 12:04
  • @black The size of the bitfield type is not necessarily related to the size of the struct. Anyway, on a small microcontroller, `bool` is very likely to be 1 byte. – Potatoswatter Apr 25 '15 at 12:28
0

Signed and unsigned integers are the answer.

Keep in mind that signaling is just an interpretation of bits, -1 or 1 is just the 'print' serializer interpreting the "variable type", as it was "revealed" to cout functions (look operator overloading) by compiler, the bit is the same, its value also (on/off) - since you have only 1 bit.

Don't care about that, but is a good practice to be explicit, so prefer to declare your variable with unsigned, it instructs the compiler to mount a proper code when you set or get the value to a serializer like "print" (cout).

"COUT" OPERATOR OVERLOADING: "cout" works through a series of functions which the parameter overloading instructs the compiler which function to call. So, there are two functions, one receives an unsigned and another signed, thus they can interpret the same data differently, and you can change it, instructing the compiler to call another one using cast. See cout << myclass

Community
  • 1
  • 1
Luciano
  • 2,695
  • 6
  • 38
  • 53