Recently I was helping a friend on project that included C++ and Java. The C++ code was for an embedded micro controller that only had 2K of dynamic memory at runtime.

Since we were still in the early prototyping stages the memory use was pretty much all global variables for the major components of the project. The 2K of memory was also where our runtime stack lived so all of the stack address calls and local function variables would be taken from here as well. Not a lot of room to be sloppy if you plan on creating anything complex.

Several of the items were either static or dynamic arrays that we knew were going to get larger as the project matured and we knew we would want to revisit the actual implementation and optimize it as the object definitions started to settle down.

This project happened to be a game and as the user progressed through the game the amount of runtime memory used would grow as the complexity of each level increased. So we knew that in the final version we would want as much dynamic memory available as possible. Once the basic memory use for just starting the game grew to over 1K it was time to start examining what was being used and how to shrink it down.

Variable Sizes

As is customary when you start to optimize the size of your structures and classes one of the first places I started was to examine the storage type and length for the various state data needed. Examine all integers and if their values will never be larger than 255 make sure they are stored as unsigned or signed char. Make sure no long int’s are being used when a standard int or even a short int can suffice. Things like that.

After completing a rough first pass it became evident that quite a few boolean variables would always be needed. My first thought was to check on the default size of a bool. It turned out to default to a standard int which for this micro processor was 2 bytes.

This was obviously a waste and I briefly thought that they should all be changed to an unsigned char and we’d just keep a 1 or 0 value. But this still wasted 7 bits for each bool and the combined waste for all of them was just too offensive to ignore so I decided to use plain C bitfields for them.

I had not used or needed bitfields in many years and as I started to make more use of them I was reminded how powerful a tool they are to reduce memory usage. This is of particular importance in embedded projects where you only have 2048 bytes or less to begin with.

Bitfields : A Brief Refresher

If you recall, bitfields in C and C++ allow you to specify exactly how many bits an integer value will occupy. To do this you declare the integer as you normally would but after the variable name you include a colon : and the number of bits to be used followed by the terminating semicolon:

int value1 : 2;

In this case we’ve declared a signed integer that will have 2 bits. This gives us a total of 4 binary values we can store in value1 and no more. If we ever need more than that in the future we would need to increase the number of bits used. Fortunately due to the nature of binary values we double the number of binary values available for each bit we increase in size so it’s easy to be precise in our usage without much slack (unused combinations). Note that I pointed out this was a signed int. It might not be immediately obvious but that can get interesting as we will see later on.

For boolean values we get the best return for our buck because we can occupy exactly one bit only to store a 1 bit piece of information:

int completed : 1;

In our example project this meant that to keep say, 5 boolean values we would go from requiring 10 bytes of memory down to one byte, saving 9 bytes of memory right away. And even this single byte had 3 bits remaining for us to use later. Even better, these boolean values were actually parts of a structure we kept an array of 24 at a time! So we gained 9 bytes of memory back for every item in the array returning a whopping 216 bytes back to us right away!

Going Further..

So after I replaced all of the boolean flags everywhere to be single bits I started to examine the remaining variables stored for each item.

Returning to the previous thought that numbers below 256 can be stored in a single byte, you can go further for variables that will only keep even lower values. For example if a value will only be in the range of 0 to 7 then we will only need 3 bits for the variable that holds it.

Several of the values we used in the project were indexes into the 24-entry array so they never held a value outside of the range of 0 to 23. All of these could be declared to only require 5 bits since that can store a number from 0 to 31.

A few values needed were the results of analog to digital conversion (ADC) readings for various sensors. The ADC for this particular micro processor was 10 bits which meant that all analog reads would always be in the range of 0 to 1023. Might as well take advantage of that as well and only use 10 bits for those stored values. Things were starting to pay off nicely.

Note that just like all C/C++ declarations you can declare multiple variables of the same type on one or more lines by separating them with commas. This can be help to keep bitfield declarations easy to read and maintain:

struct ExampleItem { unsigned int available : 1, // flags.. selected : 1, hit : 1, visible : 1, completed : 1, size : 5, // 0 - 23 index light : 10; // 0 - 1023 // default constructor ExampleItem() : available(0), selected(0), hit(0), visible(0), completed(0), size(0), light(0) { } }; ExampleItem items[24];

Note the tiny memory usage! :

sizeof(ExampleItem) == 3 sizeof(items) == 72

just 3 bytes for 7 variables!

Last Thoughts

One thing to note about bitfields that I didn’t expect was the implication of using signed vs unsigned int fields. As I was making my initial changes I did a quick sanity check to be sure that if a bitfield was incremented and it caused an overflow, that it would simply wrap around to 0 and would not affect the bitfield after it. So I ran this quick test:

... struct { int value : 2, complete : 1; } item = {0, 0}; for (int loop=0; loop < 8; loop++) { printf("item.value = %d, item.complete := %d", item.value++, item.complete++); } ...

and I was expecting to see item.value cycle through 0 to 3 and then wrap around to 0 but instead I got this:

item.value = 0, item.complete := 0

item.value = 1, item.complete := -1

item.value = -2, item.complete := 0

item.value = -1, item.complete := -1

item.value = 0, item.complete := 0

item.value = 1, item.complete := -1

item.value = -2, item.complete := 0

item.value = -1, item.complete := -1

I hadn’t even thought about the implication of the highest bit being used as the sign bit and certainly hadn’t expected that output. Especially having a 1 bit field which was treated purely as a sign bit with no bits for value as is the case with the item.complete field!

Changing to use unsigned values gave the results I had originally expected:

item.value = 0, item.complete := 0

item.value = 1, item.complete := 1

item.value = 2, item.complete := 0

item.value = 3, item.complete := 1

item.value = 0, item.complete := 0

item.value = 1, item.complete := 1

item.value = 2, item.complete := 0

item.value = 3, item.complete := 1

One final note about using bitfields is that the size modifier for the int field has no affect on the resulting number of bytes used (when using an 8-bit micro controller). The size will always be the number of bytes required for all bitfields, rounded to the next 8-bit boundary if necessary. So these two structures both occupy only one byte regardless of the size modifier on the int declaration line:

struct StructOne {

unsigned long int value : 1;

} one;



struct StructTwo {

unsigned char value : 1;

} two;



printf("sizeof(one) = %d

", sizeof(one));

printf("sizeof(two) = %d

", sizeof(two));

Both result in the same output:

sizeof(one) = 1 sizeof(two) = 1

So use bitfields in your embedded classes and structure’s int definitions wherever possible. You’ll thank yourself in the future!