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; }