结构体在进行编译时,编译器(gcc)会对结构体中的成员进行对齐。对齐大小默认按照结构体中大小最大的成员进行对齐。
typedef struct {
char* name; // 8
int height; // 4
int width; // 4
} box1_t;
typedef struct {
int width; // 4
char* name; // 8
int height; // 4
} box2_t;
对于两个含有相同的成员的结构体,由于存在对齐,最终的结构体大小也会不一样。编译器在编译时,不会智能的对结构体成员进行排序,而是按照结构体中成员的定义顺序进行对齐。
box1_t
+-----------------+-----------------+
| name | height | width |
+-----------------+-----------------+
| 8 | 4 | 4 |
+-----------------+-----------------+
box2_t
+-----------------+-----------------+-----------------+
| width | name | height |
+-----------------+-----------------+-----------------+
| 8 | 8 | 8 |
+-----------------+-----------------+-----------------+
由于两个结构体中最大的元素大小均为char*
,即大小为8个字节,所以每个元素的大小都将对齐为8字节,如果两个相邻的元素所占大小小于等于8个字节,那么就可以将两个元素放在一起,一共占用8字节,否则则需要两个8字节。
通过#pragma pack()
宏可以修改对齐大小
#pragma pack(2) // 2 即为4个字节
typedef struct {
int width;
char* name;
int height;
} box3_t;
#pragma pack()
此时在内存中,结构体成员的对齐方式就变成了这样
box3_t
+--------+---------+-------+----------+
| width | name | height |
+--------+---------+-------+----------+
| 4 | 8 | 4 |
+--------+---------+-------+----------+
结构体对齐的原因主要是为了方便内存的读取,当内存对齐都为8字节时,可以确保任何一个结构体成员都可以通过一次内存读取获得,而当内存对齐为4字节时,读取小于等于4个字节以下的元素可以保证一次读取,但当读取一个8字节的成员时,可能会出现两次读取,这将会导致程序运行速度变慢。这也是编译器的无奈之举,速度快,那么内存占用必然会变得很大,反之,内存占用小,程序可能会需要更多的时间执行。
#include <stdio.h>
typedef struct {
char* name; // 8
int height; // 4
int width; // 4
} box1_t;
typedef struct {
int width; // 4
char* name; // 8
int height; // 4
} box2_t;
#pragma pack(2)
typedef struct {
int width;
char* name;
int height;
} box3_t;
#pragma pack()
int
main()
{
printf("sizeof(box1_t) = %lu\n", sizeof(box1_t));
printf("sizeof(box2_t) = %lu\n", sizeof(box2_t));
printf("sizeof(box3_t) = %lu\n", sizeof(box3_t));
}
// sizeof(box1_t) = 16
// sizeof(box2_t) = 24
// sizeof(box3_t) = 16