Skip to main content
Associate II
February 23, 2024
Solved

Phantom 16bit variable inserted by compiler in struct?

  • February 23, 2024
  • 3 replies
  • 3798 views

Hi, I have a very weird problem. I will narrow it down as much as I can. STM32F767.

I have a struct with various variables, total is 30 bytes. But somehow, the sizeof(struct) is 32!

 

typedef struct {
 uint32_t var1; // 0
int16_t var2; // 4
int16_t var3; // 6
int16_t var4; // 8
int16_t var5; // 10
uint8_t var6; // 12
uint8_t var7; // 13
uint16_t var8; // 14
int16_t var9; // 16
int16_t var10; // 18
int16_t var11; // 20
int16_t var12; // 22
int16_t var13; // 24
uint32_t var14; // 26
} data_structure;

 

data_structure my_data;
memset (msg, 0, 64);
sprintf (msg, "Size of my_data: %d\r\n", sizeof(my_data));
HAL_UART_Transmit(&huart6, msg, strlen(msg), 100);

 

Returns:

Size of my_data: 32

 

The actual problem (after tests) is that the compiler inserts a "phantom" 16bit variable between var13 and var14 - pushing my uint32_t var14 2 bytes further and causing me issues. I'm sure this is my fault somehow but I don't see anything wrong?

 

Here's how I figured this out - I wrote a small piece of code that essentially increments the value of each byte sequentially in memory, by assigning the proper values in the variables of the structure, then printed each byte, one by one.

my_data.var1 = 0x04030201;
my_data.var2 = 0x0605;
my_data.var3 = 0x0807;
my_data.var4 = 0x1009;
my_data.var5 = 0x1211;
my_data.var6 = 0x13;
my_data.var7 = 0x14;
my_data.var8 = 0x1615;
my_data.var9 = 0x1817;
my_data.var10 = 0x2019;
my_data.var11 = 0x2221;
my_data.var12 = 0x2423;
my_data.var13 = 0x2625;
my_data.var14 = 0x30292827;

memset (msg, 0, 32);
sprintf (msg, "Size of my_data: %d\r\n", sizeof(outpc));
HAL_UART_Transmit(&huart6, msg, strlen(msg), 100);

p = (char *)&my_data;

for (i = 0; i < sizeof(my_data); i++)
{
buffer[i] = *(p+i);

memset (msg, 0, 32);
sprintf (msg, "%i: 0x%02X\r\n", i+1, *(p+i));

HAL_UART_Transmit(&huart6, msg, strlen(msg), HAL_MAX_DELAY);
}

Here's the output: 27 and 28 should not be there!

Size of my_data: 32
1: 0x01
2: 0x02
3: 0x03
4: 0x04
5: 0x05
6: 0x06
7: 0x07
8: 0x08
9: 0x09
10: 0x10
11: 0x11
12: 0x12
13: 0x13
14: 0x14
15: 0x15
16: 0x16
17: 0x17
18: 0x18
19: 0x19
20: 0x20
21: 0x21
22: 0x22
23: 0x23
24: 0x24
25: 0x25
26: 0x26
27: 0x78
28: 0x56
29: 0x27
30: 0x28
31: 0x29
32: 0x30

 

 

    Best answer by TDK

    32-bit variables are generally aligned to 32-bits by the compiler in order to improve code speed and efficiency and, in some cases, because unaligned access is not allowed. That is why the additional space is there. In addition, because the struct has 32-bit variables, the compiler will make the entire struct a multiple of 32-bits.

    If you don't want the space, you can specify that the struct be packed with __attribute__((packed)):

     

    typedef struct {
     uint32_t var1; // 0
    int16_t var2; // 4
    int16_t var3; // 6
    int16_t var4; // 8
    int16_t var5; // 10
    uint8_t var6; // 12
    uint8_t var7; // 13
    uint16_t var8; // 14
    int16_t var9; // 16
    int16_t var10; // 18
    int16_t var11; // 20
    int16_t var12; // 22
    int16_t var13; // 24
    uint32_t var14; // 26
    } data_structure;
    
    typedef struct __attribute__((packed)) {
     uint32_t var1; // 0
    int16_t var2; // 4
    int16_t var3; // 6
    int16_t var4; // 8
    int16_t var5; // 10
    uint8_t var6; // 12
    uint8_t var7; // 13
    uint16_t var8; // 14
    int16_t var9; // 16
    int16_t var10; // 18
    int16_t var11; // 20
    int16_t var12; // 22
    int16_t var13; // 24
    uint32_t var14; // 26
    } data_structure_2;
    
    int main()
    {
     printf("sizeof(data_structure)=%d\n", (int)sizeof(data_structure));
     printf("sizeof(data_structure_2)=%d\n", (int)sizeof(data_structure_2));
    
     return 0;
    }

     

     which yields

     

    sizeof(data_structure)=32
    sizeof(data_structure_2)=30

     

     

    3 replies

    Peter BENSCH
    Technical Moderator
    February 23, 2024

    If the compiler did not do this, the variable var14 would not be 32bit aligned, i.e. torn into two 16bit parts.

    Regards
    /Peter

    In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
    Associate II
    February 23, 2024

    Hi @Peter BENSCH 

    I'm not sure I understand your reply.

    I stripped my program down to just the serial port and the struct init and serial print. I have attached it to this post. If anyone can have a look and hopefully tell me where the problem is, I would be grateful. I haven't seen anything like this for quite a while.

    TDK
    TDKBest answer
    Super User
    February 23, 2024

    32-bit variables are generally aligned to 32-bits by the compiler in order to improve code speed and efficiency and, in some cases, because unaligned access is not allowed. That is why the additional space is there. In addition, because the struct has 32-bit variables, the compiler will make the entire struct a multiple of 32-bits.

    If you don't want the space, you can specify that the struct be packed with __attribute__((packed)):

     

    typedef struct {
     uint32_t var1; // 0
    int16_t var2; // 4
    int16_t var3; // 6
    int16_t var4; // 8
    int16_t var5; // 10
    uint8_t var6; // 12
    uint8_t var7; // 13
    uint16_t var8; // 14
    int16_t var9; // 16
    int16_t var10; // 18
    int16_t var11; // 20
    int16_t var12; // 22
    int16_t var13; // 24
    uint32_t var14; // 26
    } data_structure;
    
    typedef struct __attribute__((packed)) {
     uint32_t var1; // 0
    int16_t var2; // 4
    int16_t var3; // 6
    int16_t var4; // 8
    int16_t var5; // 10
    uint8_t var6; // 12
    uint8_t var7; // 13
    uint16_t var8; // 14
    int16_t var9; // 16
    int16_t var10; // 18
    int16_t var11; // 20
    int16_t var12; // 22
    int16_t var13; // 24
    uint32_t var14; // 26
    } data_structure_2;
    
    int main()
    {
     printf("sizeof(data_structure)=%d\n", (int)sizeof(data_structure));
     printf("sizeof(data_structure_2)=%d\n", (int)sizeof(data_structure_2));
    
     return 0;
    }

     

     which yields

     

    sizeof(data_structure)=32
    sizeof(data_structure_2)=30

     

     

    "If you feel a post has answered your question, please click ""Accept as Solution""."
    Associate II
    February 23, 2024

    That makes perfect sense! I've been writing C for 8bit and 16bit uCs for almost 20 years now, and I've never had this issue before, it's always fascinating to learn something new every day.

    Andrew Neil
    Super User
    February 24, 2024

    It's been a thing from before the first ANSI C was published:

    AndrewNeil_0-1708789933295.png

     

    Often referred to as "padding"

    https://en.wikipedia.org/wiki/Data_structure_alignment

    https://www.geeksforgeeks.org/structure-member-alignment-padding-and-data-packing/

     

    A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.