3

I am implementing a radio standard and have hit a problem with unions in structure and memory size. In the below example I need this structure to located in a single byte of memory (as per the radio standard) but its currently giving me a size of 2 bytes. After much digging I understand that its because the Union's "size" is byte rather than 3 bits...but havent worked out a way around this. I have looked at:

But neither seem to give me a solution.

Any ideas?

Thanks!

#ifdef WIN32
#pragma pack(push)
#pragma pack(1)
#endif

typedef struct three_bit_struct
{
    unsigned char bit_a : 1;
    unsigned char bit_b : 1;
    unsigned char bit_c : 1;
}three_bit_struct_T;


typedef union
{
    three_bit_struct_T three_bit_struct;
    unsigned char another_three_bits : 3;
}weird_union_T;


typedef struct
{
    weird_union_T problem_union; 
    unsigned char another_bit : 1;
    unsigned char reserved : 4;
}my_structure_T;


int _tmain(int argc, _TCHAR* argv[])
{
     int size;

     size = sizeof(my_structure_T);

     return 0;
}


#ifdef WIN32
#pragma pack(pop)
#endif
Community
  • 1
  • 1
  • [This](http://stackoverflow.com/questions/6043483/why-bit-endianness-is-an-issue-in-bitfields/6044223#6044223) answers your question. Simply put: bit-fields are la-la land and can only be used for boolean flags. If used for any other purpose, they will behave in random, unpredictable, non-standardized ways. – Lundin Nov 21 '12 at 12:43
  • This will be run on an embedded processor so not worried about portability. – user1841904 Nov 21 '12 at 12:46
  • You never re-use code between projects? You never change compiler? Anyway, the problem is that nobody here can answer your question without, reading the documentation for your specific compiler. – Lundin Nov 21 '12 at 13:06
  • I work for a company that makes the RF microcontroller....so no we never change chips :) My question wasnt about bit fields, it was about the union always being a byte rather than 3 bits. – user1841904 Nov 21 '12 at 13:09
  • It is related to bit fields. sizeof(a_bit_field_that_isnt_int) is **completely implementation-defined**. Furthermore, the compiler is free to add padding _bytes_ anywhere inside your bit field, so that could create problems as well. – Lundin Nov 21 '12 at 15:05
  • Read the C standard annex J, J.3.9. – Lundin Nov 21 '12 at 15:09

2 Answers2

6

The problem is that the size of three_bit_struct_T will be rounded up to the nearest byte* regardless of the fact that it only contains three bits in its bitfield. A struct simply cannot have a size which is part-of-a-byte. So when you augment it with the extra fields in my_structure_T, inevitably the size will spill over into a second byte.

To cram all that stuff into a single byte, you'll have to put all the bitfield members in the outer my_structure_T rather than having them as an inner struct/union.

I think the best you can do is have the whole thing as a union.

typedef struct
{
    unsigned char bit_a : 1;
    unsigned char bit_b : 1;
    unsigned char bit_c : 1;
    unsigned char another_bit : 1;
    unsigned char reserved : 4;
} three_bit_struct_T;

typedef struct
{
    unsigned char another_three_bits : 3;
    unsigned char another_bit : 1;
    unsigned char reserved : 4;
} another_three_bit_struct_T;

typedef union
{
    three_bit_struct_T three_bit_struct;
    another_three_bit_struct_T another_three_bit_struct;
} my_union_T;

(*) or word, depending on alignment/packing settings.

Graham Borland
  • 60,055
  • 21
  • 138
  • 179
  • Hi Graham. Would it be possible to give an example? I need to be able to access these are bits and as the uchar. Thanks! – user1841904 Nov 21 '12 at 12:35
0

Two good advices: never use struct/union for data protocols, and never use bit-fields anywhere in any situation.

The best way to implement this is through bit masks and bit-wise operators.

#define BYTE_BIT7 0x80u

uint8_t byte;

byte |= BYTE_BIT_7;  // set bit to 1
byte &= ~BYTE_BIT_7; // set bit to 0

if(byte & BYTE_BIT_7) // check bit value

This code is portable to every C compiler in the world and also to C++.

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Will have to disagree on both of those at least in this case :) Readability is very very important in this code as its part of an ISO radio standard. – user1841904 Nov 21 '12 at 12:52
  • @user1841904 If you can't read or understand bit-wise operators, you shouldn't go near an embedded system. Apart from that, how is your three_bit_struct readable? Please tell me. Does it mean, here are bits 7,6,5 or bits 5,6,7 or bits 0,1,2, or bits 2,1,0. Or does it mean, do something completely implementation-specific? One cannot tell by reading your code, and one cannot tell it by reading the C standard either. So how exactly is your code more readable? – Lundin Nov 21 '12 at 13:11
  • If you hadnt already realised, I had anonomised the code before posting it as an example. Its part of a much larger structure which is more reader friendly having it as a bitfield. – user1841904 Nov 21 '12 at 13:20
  • @user1841904 You are missing the point: no person can tell what a bit-field does by reading the code, since bit-field implementations are pretty much 100% non-standard. It is just like writing code relying on undefined behavior, but expect a certain deterministic behavior. – Lundin Nov 21 '12 at 14:57
  • @Lundin: I really wish the authors of the C standards had specified a means of aliasing bit fields to particular spots within a variable (e.g. `unsigned(1) CARRY : STATUS.7;` or `unsigned(8) msb : ll.24;`) Such bitfields could have been fully portable while allowing compilers to take advantage of any available partial-register-update instructions (a compiler would likely be more able to optimize `quad.msb = ch;` than `quad.msb ^= ((quad.msb ^ (ch << 24)) & 0xFF000000);` – supercat Feb 05 '13 at 15:45
  • @supercat If we forget the unpredictable behavior of bit-fields, then there is absolutely no difference in the way the compiler optimizes bit fields versus bit-wise operators. The difference is that some beginner programmers find the bit-wise syntax confusing. I find it unlikely that a compiler would translate your two examples into different machine code, this kind of code is typically optimized well even by poor compilers. – Lundin Feb 06 '13 at 12:42
  • @Lundin: Optimization of such things is apt to be hit-or-miss, especially if any part of the expression is anything other than a simple non-volatile variable or constant. More significantly, it would be awkward in standard C to write code that needed to use a carry flag kept in STATUS.7 in such a way that moving that flag to AUXSTATUS.3 would require nothing more than changing a header file. One would have to either use two identifiers for the address and bit offset, use a nasty macro which contains those two things separated by a comma, or specify a means... – supercat Feb 06 '13 at 16:34
  • ...of combining the address and bit number into a numeric quantity, and then use macros or functions to convert such a number to an address and bit mask as needed. The last approach is the one I've seen least, though I like it the best. Another option would be to use a C++ compiler, but mostly code in C; use macros to wrap I/O bits in special constant class instances. This latter approach can be handy writing code which is targeted for a real machine which includes extensions to map single-bit identifiers onto other addresses, but should also be able to run on the PC in a system mockup. – supercat Feb 06 '13 at 16:52