RohitMat
RohitMat

Reputation: 165

Best practice for struct layout in C/C++

I have read articles and SO answers about how we can cutoff compiler added padding if we layout the structs in the decreasing order of size of independent fields.

eg: Instead of laying out (L1) the struct like this

   typedef struct dummy {
   char         c1;
   short        s1;
   int          i1;
   unsigned int ui1;
   double       d1;
   } dummy_t;

create the layout (L2) like this

   typedef struct dummy {
   double       d1;   
   unsigned int ui1;
   int          i1;
   short        s1;
   char         c1;   
   } dummy_t;

My question here is, are there scenarios where layout L2 has downsides when compared to L1 or any other layout ? Or can I take it into my design rules to always use L2 ? Also is the natural alignment rule

natural alignment = sizeof(T)

always true for primitive types ?

Upvotes: 0

Views: 2456

Answers (3)

0___________
0___________

Reputation: 67476

IMO abstracting from the implementation details like caching (which is too broad to be discussed in the post answer) there is no difference.

The difference is if you place the variable sized (or zero sized) object at the end of the struct (example):

typedef struct
{
    size_t size;
    char data[];
}string;

string *create(size_t size)
{
    string *ptr = malloc(sizeof(*ptr) + size);
    if(ptr)
    {
        ptr -> size = size;
    }
    return ptr;
}

If course the logical member order (and potential packing) is required if struct will store some received binary data (for example communication packet header)

Upvotes: 1

Eric Postpischil
Eric Postpischil

Reputation: 222272

My question here is, are there scenarios where layout L2 has downsides when compared to L1 or any other layout ?

Sometimes you need to have members in a different order. Reasons for this may include:

  • The structure is part of a communications protocol and has to be sent byte-by-byte across a network or other communication device. In this case, the layout of the structure may be dictated by the protocol, and you will have to match it exactly (including padding or lack thereof).
  • The structure is part of a family of structures that share initial sequences of members, so those members must be placed first. This is sometimes done to allow structures to be handled in a “generic” way by operating on those initial members only.
  • The structure includes a buffer whose length will vary (by being allocated dynamically according to needs that arise during program execution). Such a buffer is implemented with a flexible array member, which must be the last member in the structure.

Also, there may be incidental effects of how members are ordered. For example, if member x happens to be used much more frequently than other members of the structure, putting it at the front might allow the compiler to access it with simpler address arithmetic, since its offset from the beginning of the structure will be zero. This is rarely a consideration in programming, but I mention it for completeness.

Aside such considerations, you are generally free to order members as you desire.

Also is the natural alignment rule natural alignment = sizeof(T) always true for primitive types ?

No. As an example, an eight-byte long might have an alignment require of one, two, four, or eight bytes.

… we can cutoff compiler added padding if we layout the structs in the decreasing order of size of independent fields.

This is not true for members that are aggregates (arrays, structures, and unions). Consider that a member char x[13]; is 13 bytes in size but only requires alignment of one byte. To minimize padding, order members in order of decreasing alignment requirement, not decreasing size.

Upvotes: 1

Jean-Marc Volle
Jean-Marc Volle

Reputation: 3323

You should wonder about alignement/structure size only if you are running on a memory constrained system (you want to limit the size of each element). This makes sense only for systems/designs where you know you will store a lot of such structure and used storage space is an issue. On the other hand grouping the content of a structure logically can bring clarity to your code. I would always favor clarity against performance, especially at early design phases. Even more if you do not have performance related constraints.

About performance it might happen that accessing "random" content from L2 is worse than from L1 because memory cache alignement constraints can also come into play. (eg accessing in a row 2 elements of struct L1 can be slower or quicker than 2 elements of L2). For such optimization, profiling is the only way to know which layout is the best.

Upvotes: 4

Related Questions