tags:

views:

208

answers:

8

I recently wrote some code that uses the same unsigned short to store two values, a result and an id, for example:

unsigned short data = new_id();
// result is either 0 or 1 so store it in the rightmost bit and move the id left
data = (data << 1) + get_result();
// ... later ...
// now we can print results like
printf("%u: %u\n", data & 1, data >> 1);

Would it be better to just use a struct to hold both values or is this type of thing common/acceptable? The program already stores so much memory I thought I'd start to find ways to decrease the memory it uses up.

+7  A: 

Unless memory is incredibly tight, I would go the struct route. It's a lot clearer that way and easier for the next person who has to maintain your code.

I'm reminded of the time when the M68000 had 32 bit address registers but only 24 bits of them were actually used. Programmers did all sorts of "optimizations" to store information in those other 8 bits. Boy were their faces red when later versions of the chip, like the M68030, used all 32 bits.

Paul Tomblin
+1, always think on the next person.
Arkaitz Jimenez
+1. Think of the maintainer as an axe wielding maniac who knows where you live.
Yacoby
@Yacoby, especially if it's going to be me.
Paul Tomblin
@Paul, OS/360 (and its descendants) made use of the upper 8 bits of 32-bit addresses for storing various flags, at least until IBM introduced 31-bit addressing CPUs. Most common was the upper bit was set to indicate the last arg in the arg list passed to a procedure.
Loadmaster
+13  A: 

Bitfields (but only if you really need to be tight on space - ie embedded systems)?

typedef struct id_result {
    unsigned int id : 15;
    unsigned int result : 1;
} id_result;

otherwise, yes, use a struct with more complete and meaningful definitions:

typedef uint16 IDTYPE; /* assuming uint16 exists elsewhere */

typedef struct id_result {
    IDTYPE id;
    bool result;
} id_result;
plinth
+1: This was my first thought, but you beat me to it.
Ben S
It should however be mentioned that this isn't platform independent.
Ben S
Note that you should rather use unsigned int instead of int, as an int on a single bit can only have values 0 or -1.
Didier Trosset
@dtrosset - noted and changed.
plinth
@Ben S: as far as platform independence goes, bitfields are fine as long as you aren't trying to pass the 'combined' values to another system (or even another program on the same system) or to map them to hardware or for building/parsing protocol packets. If the bitfields are used only within the same program there's no problem.
Michael Burr
+2  A: 

Unless there is absolutely crunch of memory, I would rather go for simpler approach of having structure with two different variables. It increases the readability and reduces the maintenance efforts.

aJ
+1  A: 

Fitting both shorts into a single short yourself is, in my opinion, more work and more error-prone than using a struct. If using less memory is really required, you can specify bitfields with a struct :

struct myStruct {
int data:8;
int result:8;
};

It achieves the same memory-reducing result, while increasing the overall maintainability of the code.

Fred
+2  A: 

If this is done solely for the purpose of reducing the memory usage, then I believe you should not do it. You are better off using a structure with 2 shorts which makes the code much more readable. The amount of memory you save by doing this very tiny compared to the benefits you get by making the code more maintainable.

I suggest you first profile the system to find out whether there are any memory leaks or whether somebody is unnecessarily allocating large chunks of memory, etc., and then try to solve that problem. If still you can't find a solution, then find out which part of your program takes up most of the memory and try to redesign its memory allocation model.

Naveen
+1  A: 

To those who advise against conserving memory with bitfields: as the years go by and computers get more gigabytes, L1$ (the fast memory) remains just a few dozen kilobytes. For most applications today, a majority of time is spent waiting for slow memory to arrive in the L1$.

Since slow memory is the bottleneck in most applications, conserving memory with bitfields can actually significantly increase an application's speed. This wasn't as true twenty years ago.

bmcnett
This is true, but I'm not sure it's relevant. Modern computers almost always fetch into L1 cache in chunks sized by the machine word, and so with most computers having 32-bit or 64-bit processors, making a value smaller than 32 bits doesn't help you a lot. This is definitely a case where premature optimization could be the cause of a great deal of evil. Better to try the simple (most maintainable) way first, and only go to something more complex if you determine you truly need it.
Daniel Pryden
Sure they fetch in chunks, but if each fetched chunk holds 2x as much data, the L1$ is likely to hit 2x as many times between fetches. Storing data in 32 bits by default should be seen as premature pessimization, because the cache so dominantly bottlenecks performance.
bmcnett
A: 

Structs with bitfields are the most understandable implementation. If you don't use that approach, you could use a set of well-documented macros that pack and unpack the value pair into and out of 16-bit values.

Loadmaster
A: 

Using structs/objects isn't necessarily the best or clearest approach.

Suppose you have a set of simple integer data points but they can be deleted or marked invalid, if you simply use the MSB to flag as 'don't use' then all you need to add to the algorithm is a

if ( item > 0 )
   item += blah

But if you have a struct then every bit of arithmatic now needs a member access

if ( item.valid() ) 
   item.setValue(item.getValue() + blah);
Martin Beckett