2024-02-23 12:38 PM
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
Solved! Go to Solution.
2024-02-23 01:50 PM - edited 2024-02-23 01:50 PM
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
2024-02-23 12:59 PM
If the compiler did not do this, the variable var14 would not be 32bit aligned, i.e. torn into two 16bit parts.
Regards
/Peter
2024-02-23 01:13 PM
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.
2024-02-23 01:50 PM - edited 2024-02-23 01:50 PM
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
2024-02-23 01:55 PM
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.
2024-02-24 07:52 AM - edited 2024-02-24 07:56 AM
It's been a thing from before the first ANSI C was published:
Often referred to as "padding"
https://en.wikipedia.org/wiki/Data_structure_alignment
https://www.geeksforgeeks.org/structure-member-alignment-padding-and-data-packing/
2024-02-24 08:05 AM
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.
2024-02-24 12:38 PM
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
2024-02-24 12:57 PM
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.
2024-02-26 02:22 AM - edited 2024-02-26 02:26 AM
@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 ...