cancel
Showing results for 
Search instead for 
Did you mean: 

Phantom 16bit variable inserted by compiler in struct?

Tech_Engineer
Associate II

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

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

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".

View solution in original post

9 REPLIES 9
Peter BENSCH
ST Employee

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.
Tech_Engineer
Associate II

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
Guru

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".

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.

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/

 

It's been a while since I read K&R's bible. 25 years or more. These small details sometimes elude me. That's why I still keep these aging books in my library.

This was a thing with 80386's 30 years ago.. and with MIPS / SPARQ / ARM7/9 RISC

Misaligned reads / writes slow things down, so structures in memory need to be optimized for access, whereas you want to pack structures at byte levels for files, storage and data transmission.

Cortex-M0 particularly intolerant of alignment

ARM ABI wants 8-byte / 64-bit alignments, but more of a long-term compatibility / future-proof

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Other thing is don't use 8 or 16-bit variables for counters etc, for the sake of "size", it's not efficient

Use int's in for / while loops, or as array indexes, the code will be simpler, cleaner and faster.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

@Tesla DeLorean wrote:

Other thing is don't use 8 or 16-bit variables for counters etc, for the sake of "size", it's not efficient


This often catches-out people coming from 8-bit micros!

Could also look at int_fast8_t et  al from stdint.h ...

https://cplusplus.com/reference/cstdint/