bsh152s
bsh152s

Reputation: 3218

Byte allocation in struct containing struct within C

I have the following structs in a C program.

typedef struct
{
  unsigned char msg_id : 8;
  unsigned char msg_num : 8;
} Message_Header_Type;
typedef struct
{
  Message_Header_Type header;
  int msg[32];
} Raw_Message_Type;

What I'm seeing is that the header in the Raw_Message_Type is taking up 4 bytes but I only want it to take only 2. How would I go about doing this?

Upvotes: 1

Views: 79

Answers (4)

sergej
sergej

Reputation: 17999

What you are looking for is struct packing, which is platform and compiler dependent, the details are not specified in the C standard.

With GCC, on a 32-bit platform, you could do the following to get a struct of size 2:

typedef struct __attribute__((__packed__))
{
    unsigned char msg_id;
    unsigned char msg_num;
} Message_Header_Type;

From the GCC documentation:

packed This attribute, attached to an enum, struct, or union type definition, specified that the minimum required memory be used to represent the type.

Upvotes: 3

Josh Sanford
Josh Sanford

Reputation: 672

Message_Header_type inside of Raw_Message_Type is still taking 2 bytes as you would expect, but the compiler is padding the structure for alignment reasons, so your Raw_Message_Type is 132 bytes long instead of the 130 bytes you expected. This is probably because 32-bit aligned accesses are more efficient on your processor.

If you are using gcc, you can tell the compiler to align on 2-byte boundaries instead of the 4-byte boundaries that it is using by using #pragma pack.

typedef struct
{
  unsigned char msg_id : 8;
  unsigned char msg_num : 8;
} Message_Header_Type;
#pragma pack(push, 2) // save current alignment and set to 2-byte boundaries
typedef struct
{
  Message_Header_Type header;
  int msg[32];
} Raw_Message_Type;
#pragma pack(pop) // restore the previous alignment

Special packing like this is often necessary when using fixed-size structures in files, but be aware that there may be a (light) performance penalty for using a different packing than what the processor prefers. In this specific case, your 32 msg[] ints are now all 2 bytes off from the preferred alignment for your platform.

Upvotes: 2

sjsam
sjsam

Reputation: 21955

Structure objects are not guaranteed the size of the sum of bits used in member bit fields. This is due to padding.

In your case though Raw_Message_Type has another member int msg[32];. It seems logical to use two more bytes for the purpose alignment so that msg[0] can be aligned to a 4-byte boundary.

There is a good chance that

Message_Header_Type obj;
printf("Size : %zu\n",sizeof(obj));

would give you 2 as a the result.

Upvotes: 0

John Bollinger
John Bollinger

Reputation: 180113

C implementations are free to insert padding into struct layouts between members, at the end, or both. It is common for them to do so for alignment purposes. It is also common for compilers to provide a mechanism to influence or override the padding, but details are necessarily implementation-specific.

With GCC, for example, you could apply the packed attribute to both your structs:

typedef struct __attribute__((__packed__))
{
  unsigned char msg_id : 8;
  unsigned char msg_num : 8;
} Message_Header_Type;

typedef struct __attribute__((__packed__))
{
  Message_Header_Type header;
  int msg[32];
} Raw_Message_Type;

Some other implementations borrow GCC's approach; some use pragmas for the same purpose; and some provide other mechanisms. You'll need to check your compiler's documentation.

Upvotes: 1

Related Questions