Recently encountered a problem where ThreadX running on ARM crashed while communicating with DSP using message queues (the final implementation principle is interrupt + shared memory). After investigation, it was found that the structure used for message passing did not consider the issue of byte alignment.
Here’s a brief overview of byte alignment issues in C language to share with everyone.
1. Concept
Alignment is related to the position of data in memory. If the memory address of a variable is exactly a multiple of its length, it is called naturally aligned. For example, on a 32-bit CPU, if an integer variable’s address is 0x00000004, it is naturally aligned.
First, let’s understand bits, bytes, and words.
Name | English Name | Meaning |
---|---|---|
Bit | bit | 1 binary digit is called 1 bit |
Byte | Byte | 8 binary digits are called 1 Byte |
Word | word | A fixed length that a computer uses to process transactions at once |
Word Length
The number of bits in a word, modern computers typically have word lengths of 16, 32, or 64 bits. (Generally, the word length of an N-bit system is N/8 bytes.)
Different CPUs can process different amounts of data at once; a 32-bit CPU can process 32 bits of data at once, while a 64-bit CPU can process 64 bits of data. Here, the bits refer to the word length.
The so-called word length is sometimes referred to as a word. In a 16-bit CPU, a word is exactly two bytes, while in a 32-bit CPU, a word is four bytes. If using words as the unit, there are also double words (two words) and quad words (four words).
2. Alignment Rules
For standard data types, the address must be a multiple of its length; for non-standard data types, alignment is based on the following principles: Arrays: aligned according to basic data types, once the first is aligned, the others are naturally aligned. Unions: align according to the largest data type it contains. Structures: each data type in the structure must be aligned.
3. How to Limit Byte Alignment?
1. Default
By default, the C compiler allocates space for each variable or data unit according to its natural alignment conditions. Generally, the following methods can be used to change the default alignment conditions:
2. #pragma pack(n)
· Using the pseudo-instruction #pragma pack (n), the C compiler will align according to n bytes. · Using the pseudo-instruction #pragma pack (), cancel the custom byte alignment.
#pragma pack(n) is used to set variables to align in n byte increments. n byte alignment means that the starting address offset of the variable has two situations:
-
If n is greater than or equal to the number of bytes occupied by the variable, then the offset must meet the default alignment. -
If n is less than the number of bytes occupied by the variable, then the offset must be a multiple of n, without needing to meet the default alignment.
The total size of the structure also has a constraint: if n is greater than or equal to the number of bytes occupied by all member variable types, then the total size of the structure must be a multiple of the space occupied by the largest variable; otherwise, it must be a multiple of n.
3. __attribute
Additionally, there is the following method: · __attribute((aligned (n))) allows the structure members to be aligned on n-byte natural boundaries. If there are members in the structure whose lengths exceed n, they will be aligned according to the maximum member length. · attribute ((packed)) cancels the optimization alignment of the structure during compilation, aligning according to the actual number of bytes occupied.
4. Assembly .align
Assembly code usually uses .align to specify the byte alignment.
.align: is used to specify the data alignment method, formatted as follows:
.align [absexpr1, absexpr2]
With a certain alignment method, fill unused storage areas with values. The first value indicates the alignment method, 4, 8, 16, or 32. The second expression indicates the padding value.
4. Why Align?
The operating system does not access memory byte by byte but accesses it in word lengths such as 2, 4, or 8. Therefore, when the CPU reads data from memory to registers, the length of IO data is usually the word length. For example, the access granularity of a 32-bit system is 4 bytes, while for a 64-bit system, it is 8 bytes. When the length of the accessed data is n bytes and the data address is n bytes aligned, the operating system can efficiently locate the data at once, without needing to read multiple times or perform additional operations such as alignment calculations. Data structures should be aligned as much as possible on natural boundaries. If accessing unaligned memory, the CPU needs to perform two memory accesses.
Potential issues with byte alignment:
Many alignment issues in code are implicit. For example, during type casting. For instance:
unsigned int i = 0x12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;
p=&i;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;
The last two lines of code access an unsigned short variable from an odd boundary, which clearly does not conform to alignment regulations. On x86, similar operations only affect efficiency, but on MIPS or Sparc, it may cause an error, as they require byte alignment.
5. Examples
Example 1: Byte Size of Basic Data Types in OS
First, check the bitness of the operating systemCheck the byte size of basic data types in a 64-bit operating system:
#include <stdio.h>
int main()
{
printf("sizeof(char) = %ld\n", sizeof(char));
printf("sizeof(int) = %ld\n", sizeof(int));
printf("sizeof(float) = %ld\n", sizeof(float));
printf("sizeof(long) = %ld\n", sizeof(long));
printf("sizeof(long long) = %ld\n", sizeof(long long));
printf("sizeof(double) = %ld\n", sizeof(double));
return 0;
}

Example 2: Memory Size Occupied by Structure — Default Rules
Consider the following structure’s occupied bits
struct yikou_s
{
double d;
char c;
int i;
} yikou_t;
Execution result
sizeof(yikou_t) = 16
The relationship of each variable’s position in the content is as follows:
The position of member C is also affected by byte order, which may be at position 8
The compiler performs memory alignment for us, and the starting address offset of each member variable must be a multiple of the number of bytes occupied by the variable type, and the size of the structure must be a multiple of the number of bytes occupied by the type that occupies the maximum space in the structure.
For offset: The starting address offset of variable type n relative to the structure’s starting address must be a multiple of sizeof(type(n)) Structure size: Must be a multiple of the member’s maximum type byte
char: Offset must be a multiple of sizeof(char), i.e., 1
int: Offset must be a multiple of sizeof(int), i.e., 4
float: Offset must be a multiple of sizeof(float), i.e., 4
double: Offset must be a multiple of sizeof(double), i.e., 8
Example 3: Adjusting Structure Size
We will adjust the positions of variables in the structure as follows:
struct yikou_s
{
char c;
double d;
int i;
} yikou_t;
Execution result
sizeof(yikou_t) = 24
The layout of each variable in memory is as follows:
When the structure has nested composite members, the offset of composite members relative to the structure’s base address is a multiple of the size of the widest basic type.
Example 4: #pragma pack(4)
#pragma pack(4)
struct yikou_s
{
char c;
double d;
int i;
} yikou_t;
sizeof(yikou_t) = 16
Example 5: #pragma pack(8)
#pragma pack(8)
struct yikou_s
{
char c;
double d;
int i;
} yikou_t;
sizeof(yikou_t) = 24
Example 6: Assembly Code
Example: The following is a snippet of uboot code for the entry points of exception vectors irq and fiq:
6. Summary
For those who are lazy, here’s a complete example for you:
#include <stdio.h>
main()
{
struct A {
int a;
char b;
short c;
};
struct B {
char b;
int a;
short c;
};
struct AA {
// int a;
char b;
short c;
};
struct BB {
char b;
// int a;
short c;
};
#pragma pack (2) /* Align by 2 bytes */
struct C {
char b;
int a;
short c;
};
#pragma pack () /* Cancel specified alignment, restore default alignment */
#pragma pack (1) /* Align by 1 byte */
struct D {
char b;
int a;
short c;
};
#pragma pack ()/* Cancel specified alignment, restore default alignment */
int s1=sizeof(struct A);
int s2=sizeof(struct AA);
int s3=sizeof(struct B);
int s4=sizeof(struct BB);
int s5=sizeof(struct C);
int s6=sizeof(struct D);
printf("%d\n",s1);
printf("%d\n",s2);
printf("%d\n",s3);
printf("%d\n",s4);
printf("%d\n",s5);
printf("%d\n",s6);
}
Recommended Reading:
Hacker Shell Skills: Covering Up Operation Traces on Linux Servers
After browsing these Java communities, I floated~
How to Effectively Analyze Linux Server Performance Issues in 60 Seconds
5T Technical Resources Giveaway! Including but not limited to: C/C++, Linux, Python, Java, PHP, Artificial Intelligence, Microcontrollers, Raspberry Pi, etc. Reply “1024” in the public account to get it for free!!