1、内存对齐和填充
C语言中,编译器通常会为了提高内存访问的效率,对结构体的成员进行内存对齐。也就是编译器会按照系统的要求,将结构体中的某些成员存储在特定的内存地址上,这些地址通常是成员类型大小的整数倍。为了实现这种对齐,编译器可能会在某些成员之间插入填充字节,导致结构体的实际大小比其各个成员的大小之和要大。
内存对齐的规则,每个数据成员的地址必须是该数据类型大小的整数倍。例如,int
类型的成员通常需要存储在能被 4
整除的地址上(因为 int
通常是 4
字节)。
结构体的总大小必须是其最大对齐成员大小的倍数,这可能会在结构体末尾添加填充字节。
#include <stdio.h>
struct Example {
char a; // 1 字节
int b; // 4 字节
char c; // 1 字节
};
int main() {
printf("Size of char: %zu\n", sizeof(char)); // 1
printf("Size of int: %zu\n", sizeof(int)); // 4
printf("Size of struct Example: %zu\n",
sizeof(struct Example)); // 12(有填充)
return 0;
}
注意:按常规计算,这些成员的总和是 1 + 4 + 1 = 6
字节,但实际 sizeof(struct Example)
可能会返回 12
,而不是 6
。原因如下:
1)对齐规则
int
通常需要按 4
字节对齐,因此 char a
后面会有 3
个字节的填充,使 b
能够在 4
字节边界对齐。
2)填充规则
最后一个成员 char c
也需要填充字节来确保结构体的总大小是 int
类型(最大对齐成员)的倍数,这样在数组中下一个结构体实例也能正确对齐。
2、检查填充字节
可以通过打印各个成员的地址来查看编译器如何进行内存对齐,代码如下,
#include <stdio.h>
struct Example {
char a; // 1 字节
int b; // 4 字节
char c; // 1 字节
};
int main() {
struct Example ex;
printf("Address of a: %p\n", (void*)&ex.a);
printf("Address of b: %p\n", (void*)&ex.b);
printf("Address of c: %p\n", (void*)&ex.c);
printf("Size of struct Example: %zu\n", sizeof(struct Example));
return 0;
}
3、减少结构体中的填充字节方法
为了减少结构体的填充字节,可以通过调整成员的顺序来优化内存使用。如将较大的数据类型放在较小的数据类型之前等方法,
#include <stdio.h>
struct OptimizedExample {
int b; // 4 字节
char a; // 1 字节
char c; // 1 字节
};
int main() {
printf("Hello, World!");
return 0;
}
4、sizeof() 使用示例
使用 sizeof()
计算基本数据类型、变量、数组、指针、结构体和动态内存分配的大小。
#include <stdio.h>
#include <stdlib.h>
// 定义一个结构体
struct Person {
char name[50];
int age;
float height;
};
int main() {
// 1. 获取基本数据类型的大小
printf("基本数据类型的大小:\n");
printf("sizeof(char): %lu 字节\n", sizeof(char));
printf("sizeof(short): %lu 字节\n", sizeof(short));
printf("sizeof(int): %lu 字节\n", sizeof(int));
printf("sizeof(long): %lu 字节\n", sizeof(long));
printf("sizeof(float): %lu 字节\n", sizeof(float));
printf("sizeof(double): %lu 字节\n", sizeof(double));
// 2. 获取变量的大小
int x = 10;
double y = 20.5;
printf("\n变量的大小:\n");
printf("sizeof(x): %lu 字节\n", sizeof(x));
printf("sizeof(y): %lu 字节\n", sizeof(y));
// 3. 获取数组的大小
int arr[10];
printf("\n数组的大小:\n");
printf("数组 arr 的总大小:%lu 字节\n", sizeof(arr));
printf("数组元素个数:%lu 个\n", sizeof(arr) / sizeof(arr[0]));
// 4. 获取指针的大小
int *ptr;
char *cptr;
printf("\n指针的大小:\n");
printf("sizeof(ptr): %lu 字节\n", sizeof(ptr));
printf("sizeof(cptr): %lu 字节\n", sizeof(cptr));
// 5. 获取结构体的大小
struct Person person;
printf("\n结构体的大小:\n");
printf("结构体 Person 的大小:%lu 字节\n", sizeof(person));
printf("name 成员的大小:%lu 字节\n", sizeof(person.name));
printf("age 成员的大小:%lu 字节\n", sizeof(person.age));
printf("height 成员的大小:%lu 字节\n", sizeof(person.height));
// 6. 使用 sizeof 计算动态内存分配
// 动态分配 5 个 int 类型的内存空间
int *dynamic_array = (int *)malloc(5 * sizeof(int));
if (dynamic_array == NULL) {
printf("内存分配失败!\n");
return 1;
}
printf("\n动态内存分配的大小:\n");
printf("已分配的内存大小:%lu 字节\n", 5 * sizeof(int));
// 释放动态分配的内存
free(dynamic_array);
// 7. 使用 size_t 获取 sizeof 返回值
size_t size = sizeof(x); // 使用 size_t 接收 sizeof 返回值
printf("\nsize_t 的使用:\n");
printf("变量 x 的大小为:%zu 字节\n", size);
return 0;
}