Thanks for all the replies! I've found an answer - updated snippets posted below.
PaulS:
The :n value is a recommendation. It is not required to be recognized or used.
Interesting. I put the value 11 after one of the bit names and the compiler made no complaint. Odd, since that’s greater than the number of bits in the type. That's a tidbit to be kept.
PaulS:
An enum, for all the bit names, and bitRead() or bitSet() would do what you are trying to do with the union, with the help of a #define macro or two.
Alas, building a #define macro is not yet in my kit.
holmes4:
Err you read wrong
Mark
Ummm, I think I got that one right. Reply #1 in this thread has a link to this other thread which explains in example #3 the bit field idea.
DKWatson:
If you check sizeof TMRFlags in your first example you'll find it to be 1.
If, in example 2 you change your bool to byte, you may find memory use increases. I believe the compiler packs bools into bitfields and part of its optimization
I substituted bools for bytes in both programs – no difference in RAM usage in either.
christop:
There's only one instance of PLCtimer, and it's an automatic variable. It doesn't count toward the RAM usage report.
I did a little searching on ‘c++ automatic variable’ and didn’t find anything definitive. That’s not saying what I read was not definitive, it’s just saying it wasn’t definitive to me. Sometimes these concepts are hard to grasp!
johnwasser:
At least it is doing the mapping to bits. LSB first, it appears.
Your post and DKWatson's combined with the second link noted above jogged something loose upstairs. I added a print statement to the two snippets and got expected results. LSB first is jarring.
// Structure containing union
struct PLCtimer {
#define ton 0
#define rto 1
public:
unsigned long pre;
unsigned long acc;
unsigned long last;
union { // save RAM by combining flags into one byte
byte allbits; // allow access of the whole byte.
struct { // allow access of individual bits.
byte retentive: 1;
byte dn: 1;
byte en: 1;
byte tt: 1;
byte res: 1; // unused
byte intv: 1; //
byte tmrOS: 1; //
byte osSetup: 1;
};
} TMRFlags;
};
byte testVal;
void setup() {
// put your setup code here, to run once:
Serial.begin(230400);
PLCtimer ex1;
testVal = ex1.TMRFlags.en;
Serial.print(sizeof(ex1));
}
void loop() {
// put your main code here, to run repeatedly:
}
// Structure without union
struct PLCtimer {
#define ton false
#define rto true
public:
unsigned long pre;
unsigned long acc;
unsigned long last;
bool retentive; // TON = false, RTO = true.
bool dn; // timer has reached preset
bool en; // timer is enabled to run
bool tt; // timer is enabled and not done
bool res; // timer is reset
bool intv; // self resetting timer
bool tmrOS; // on one scan when done bit goes on
bool osSetup;
}; // end of structure PLC timer
byte testVal;
void setup() {
// put your setup code here, to run once:
Serial.begin(230400);
PLCtimer ex2;
testVal = ex2.en;
Serial.print(sizeof(ex2));
}
void loop() {
// put your main code here, to run repeatedly:
}
Size of ex1 is 13, 4 x 3 for the unsigned longs plus one for the union.
Size of ex2 is 20, 4 x 3 for the unsigned longs (12 total) plus 8 for the bits.
So, using a union does reduce RAM usage.
But it also increases flash. The un-union version reports 1456 used while the union version report 1592 bytes flash. A lot more than I'd expect.