0

I have following problem: In my code example, the too_long_CommandFrameStruct shows a size of 5, wheras all members added together have the size of 4. If I change the position of the union member to be first (like in shorter_CommandFrameStruct), the size is, like expected, 4.

Why does this happen and how can I prevent this? I'd like to have the order of the struct members as-is, because the struct gets byte-by-byte written to a (4-byte long) SPI buffer frame.

With the 5-byte long struct I have problems, because a nibble gets added between the unsigned int crc member and the union Commands cmdData. This nibble is probably causing the struct to grow to 4.5 and thus 5 bytes.

The code can be executed in an online compiler, like e.g. https://www.tutorialspoint.com/compile_c_online.php

#include <stdio.h>
#include <stddef.h>

union UnspecificFrame_t
{
    unsigned short RegValue;
    struct                  /* Single Bits */
        {
            unsigned bit0       :1;
            unsigned bit1       :1;
            unsigned bit2       :1;
            unsigned bit3       :1;
            unsigned bit4       :1;
            unsigned bit5       :1;
            unsigned bit6       :1;
            unsigned bit7       :1;
            unsigned bit8       :1;
            unsigned bit9       :1;
            unsigned bit10      :1;
            unsigned bit11      :1;
            unsigned bit12      :1;
            unsigned bit13      :1;
            unsigned bit14      :1;
            unsigned bit15      :1;
        } __attribute__((packed, aligned(1))) Bits;
};

union Commands
{
    union UnspecificFrame_t      unSpecFrame;
};

/* This one should be 4 bytes long, but actually is 5 bites long */
struct too_long_CommandFrameStruct
{
    unsigned int crc                    :4;
    union Commands              cmdData;
    unsigned int cmdType                :4;
    unsigned int id                     :8;
}__attribute__((packed, aligned(1))) too_long_CommandFrameStruct_t;

/* When switching the first members, the size is right */
struct shorter_CommandFrameStruct
{
    union Commands              cmdData;
    unsigned int crc                    :4;
    unsigned int cmdType                :4;
    unsigned int id                     :8;
}__attribute__((packed, aligned(1))) shorter_CommandFrameStruct_t;

int main()
{
    printf("why is this size: %d\n", sizeof(too_long_CommandFrameStruct_t)); // returns 5
    printf("and this one size: %d?\n", sizeof(shorter_CommandFrameStruct_t)); // returns 4

    return 0;
}
timrau
  • 22,578
  • 4
  • 51
  • 64
T.B.
  • 11
  • 4
  • Does this answer your question? [Structure padding and packing](https://stackoverflow.com/questions/4306186/structure-padding-and-packing) – Jeff Holt Sep 16 '21 at 15:01
  • 1
    Please see [Should I use bit-fields for mapping incoming serial data?](https://stackoverflow.com/q/55823879/694733) Short answer is: No. – user694733 Sep 16 '21 at 15:13
  • My guess is because I assign bit-fields within the struct, that the first bitfield gets aligned not directly in front of the struct, but actually one nibble apart, to account for correct alignment of the struct and the bitfield itself. – T.B. Sep 16 '21 at 15:22

1 Answers1

1

In C, objects other than bit-fields are represented with whole numbers of bytes. And the members of a structure are required to be in order in memory in the same order they are declared in the structure.

When unsigned int crc :4;, it does not have to fill a full byte, because it is a bit-field, but it does have to be inside one or more bytes that are used for it. Then union Commands cmdData follows that and takes two bytes. Then unsigned int cmdType :4; and unsigned int id :8; require 12 more bits, so that requires at least two bytes (presuming bytes are eight bits, as is common). So declaring the members requires at least five bytes.

Note that alignment is not even consider in the above; five bytes are required simply because you need one byte, then two, then two more.

When union Commands cmdData is first, it requires two bytes. Then the three bit-fields that follow it take 4+4+8 = 16 bits, which only need two more bytes. So the total is four bytes.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312