GoForSmoke:
Bit fields are nice but the compiler may not put the bits in the order you want.
Can somebody give an example where that would matter. You access the bitfields by their names; how the compiler has organised them does not matter in that case. I have, by the way, never seen anything else than sequential; but that does not mean too much
Below some code that I played with this morning (to find out that I needed to use padding (:() to prevent the code from crashing when casting to uint64_t). It prints the values of the struct as I expected.
/* change this according to number of contacts required
* keep as short as possible to save memory
* uint8_t max 8 contacts
* uint16_t max 16 contacts
* uint32_t max 32 contacts
* uint64_t max 64 contacts
*/
typedef uint32_t contactsize_t;
// a structure
struct REEDCONTACTS
{
contactsize_t track1: 1;
contactsize_t track2: 1;
// padding; required for Arduino and / or gcc
// adjust according to number of contacts used above
contactsize_t: sizeof(contactsize_t) * 8 - 2;
};
REEDCONTACTS currReedcontacts; // current status of read contacts
REEDCONTACTS lastReedcontacts; // previous status of reed contacts
void setup()
{
currReedcontacts.track1 = 1;
lastReedcontacts.track2 = 1;
char buffer[20];
Serial.begin(9600);
Serial.println(sizeof(contactsize_t));
contactsize_t* curr = (contactsize_t*)&currReedcontacts;
contactsize_t* last = (contactsize_t*)&lastReedcontacts;
snprintf(buffer, sizeof(buffer), "%d (%d)", sizeof(currReedcontacts), sizeof(REEDCONTACTS));
Serial.print("sizeof: "); Serial.println(buffer);
snprintf(buffer, sizeof(buffer), "%p %p", &currReedcontacts, curr);
Serial.print("addresses current: "); Serial.println(buffer);
snprintf(buffer, sizeof(buffer), "%p %p", &lastReedcontacts, last);
Serial.print("addresses last: "); Serial.println(buffer);
if (*curr == *last)
{
Serial.println("Identical");
}
else
{
Serial.println("Not identical");
// note that this does not compile if the size is uint64_t !!
Serial.println(*curr);
Serial.println(*last);
*last = *curr;
}
if (*curr == *last)
{
Serial.println("Identical");
}
else
{
Serial.println("Not identical");
}
}
void loop()
{
}
The only problem might occur when you cast the struct (as shown in the code) to a byte, int or whatever and next use binary operations (OR/shift etc). But as said, I've never seen it anything different from being sequential in which case it would not pose a problem.
Can anybody confirm that the first quoted statement is indeed the case and possibly point to compilers that exhibit the behaviour.
I was just thinking about something similar where one receives data from indeed another system (it only has to be one byte) where the compiler has shuffled the bits.
I don't think endianness is really the issue (there are functions like htons to sort that).
It matters when you read and act on the whole byte instead of one bit at a time. Simple example showing how to detect pin state change using only 1 variable for current and previous digital reads.
switch ( flagBits ) // I have 2 bits that tell current (bit 0) and prev (bit 1) pin states, simple.
{
case 0 :
// nobits set code -- no state change -- pin is and has been LOW
break;
case 1 :
// 0 bit set code -- state change detected -- pin is HIGH was LOW
break;
case 2 :
// 1 bit set code -- state change detected -- pin is LOW was HIGH
break;
case 3 :
// 2 bits set code -- no state change -- pin is and has been HIGH
break;
}
Setting the bits is done by multiplying the byte by two (left shift 1 bit is fastest way) which moves the saved current pin state (in bit 0) into the previous pin state position (bit 1) and the bit that was the previous state into bit 2 then masking off the high 6 bits (byte = byte & 3) to erase that bit and then adding the current pin state which can be 0 or 1 so only changes bit 0 to be the new current pin state. The whole operation takes very little time..
When you learn bitmath, variables become truth tables that can be evaluated in one compare.
I've been doing that since 1980, it works but you gotta know which bit is which, no shuffling!
sterretje:
Can anybody confirm that the first quoted statement is indeed the case and possibly point to compilers that exhibit the behaviour.
Here's one source saying it, you can check the C Standard if you want, Arduino uses GCC.
C standard allows compiler to put bit-fields in any order. There is not reliable and portable way to determine the order.
If you need to know the exact bit positions, it is better use plain unsigned variable and bit masking.
....... more comments/details
C99 6.7.2.1-11: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. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified. – WhozCraig Oct 15 '13 at 8:47
@WhozCraig - Yeah, sorry, as I mentioned in the first reply to you, I forgot to add this to the post. In any case, thank you for pointing the line from the standard. I guess using bit masking will be sufficient way for setting fields. I guess I can still use the structure for getting the values of the fields. – fashasha Oct 15 '13 at 8:51
Will the order of bits have any relation with endianess? – Ginu Jacob Nov 20 '14 at 9:56
1
@GinuJacob Some relation. If you do (unsigned)number & 1, it will always give you LSB, and endianness is irrelevant. Byte-endianness: If you inspect bytes of number, then byte which has LSB is implementation defined. On little-endian system, LSB can be found on first byte. Bit-endianness: It's impossible to know the order of bits in single byte in C. In short: You need to care about byte-edianness if you write code which converts data to bytes or back. You don't really need to care about bit-endianness, since C has no ability to take address of the single bit. – user694733 Nov 20 '14 at 10:26
Why bother predicting the compiler when the next one may vary where the standard lets it?
For what it's worth, avr-gcc and Visual C++ (x86) allocate bits in the opposite direction; their bit fields are not compatible.
Bear in mind that bit fields on AVR processors can be expensive (many instructions to access). They are certainly a trade-off between storage and processor time.
I'm certain that keeping track of my own flags does work and allows me some fast and powerful coding tools to build algorithms with. That's been where my point points to all along and it is something I tempt beginners to take on and start to learn when they might, kind of like how I am about state machines... that was a really good job you did for that new member, steretje.
Force consistent sizing through use of union, in this case anonymous struct/union combo, like this -
struct REEDCONTACTS
{
  union
  {
    struct
    {
    contactsize_t track1 : 1;
    contactsize_t track2 : 1;
    };
    contactsize_t force_consistant_size;
  };
};
//Flags, Use each bit in a byte as a flag bit
//i.e. one byte can contain 8 flags bits
typedef struct {
byte b0:1;
byte b1:1;
byte b2:1;
byte b3:1;
byte b4:1;
byte b5:1;
byte b6:1;
byte b7:1;
}
EightBitfield; //packs 8 flag bits in one byte
//On Arduino UNOÂ Â Â b b b b b b b b
//Â Â Â Â Â Â Â Â Â Â 0 1 2 3 4 5 6 7Â
EightBitfield flags = {1,0,1,0,0,0,0,0};Â //one byte
Can somebody give an example where that would matter.
If you want your structure to map onto some real-world thing, like a control register or data in a network packet.
My favorite example (which I discovered "the hard way" is the "Fragment Offset" field of an IP packet header:
(bytes ascending in memory from left to right.) On a nice 32bit big-endian machine like a 68000. memory layout matches the picture, and this fits nicely into a data structure:
:
unsigned int ip_id:16;
unsigned int ip_flags:3;
unsigned int ip_fragoff:13;
:
On a 32bit little-endian machine like an x86, byte swapping SPLITS the field in the middle, and there isn't any way to declare the bitfield in the structure that will work... (at least, not the last time I tried it. But I did stop trying!)
Things could be yet-again different on an 8-bit machine...)
Thanks for the replies and examples. It's clear that communication between systems will / can pose an issue. Using bitfields internally is safe though possibly expensive (need to check that out one day).
westfw:
If you want your structure to map onto some real-world thing, like a control register or data in a network packet.
EXACTLY!
This reminds me of a heated argument I had with Richard Stallman over bitfields and structure packing back in 1988 in the early days of gcc.
Back then there was no way to do forced structure element packing (disable padding) and the compiler would choose whatever it wanted. It was based on the size of the field and the native best case alignment for that processor.
Bitfields were treated similarly.
So if you had bitfields that were larger than a 8 bits but were multiples of 8 bits, like say 16 bits, the compiler would force it to be aligned on a 16 bit boundary.
For 24 bits to 32 bits it would align it to a 32 bit boundary and burn the unused 32 bits if the next member was larger than the remaining bits.
i.e. it would never span a bitfield across non aligned types.
This would create holes in the data structure and make it impossible in some cases to talk to h/w registers using bitfields.
Stallman argued that the compiler was free to do that since the C standard allowed it and it generated faster better optimized code.
I argued that you needed a way to disable that type of padding/alignment for certain real-world situations in order to use bitfields.
He came back and said if I needed that then I had bad code and needed to re-write it.
I came back and told him that this need was quite common when writing code that talked to h/w since the h/w has fixed register definitions and that there should be a way to support it.
And further, this type of code that talks to h/w registers is necessary to make things like disc controllers work and those are very necessary to run a compiler.
He was unmoved, so I went in and modified the compiler to add structure & bitfield packing support.
History has proven he was short sighted over that issue.
I will agree that in some cases it is best to do you own bit manipulation rather than use bitfields.
But for some things that is quite difficult.
I used bitfields for decoding a WWVB message frame data structure in an arduino atomic clock project. It was quite simple and efficient to do it that way as long as you layed the bits down in memory backwards to get the order correct.