1. 什么是内存对齐
内存对齐(Memory Alignment)是指数据在内存中的存储地址必须是某个值的整数倍(通常是 2、4、8 等 2 的幂次方)。现代计算机体系结构对内存访问进行了优化,要求特定类型的数据必须从特定倍数的地址开始存储。
2. 为什么需要内存对齐
2.1 硬件要求
- CPU 访问效率:多数 CPU 访问对齐的数据只需要一个总线周期,而非对齐访问可能需要多个周期。
- 硬件支持:某些架构(如 ARM)完全不允许非对齐访问,会导致硬件异常。
详细讲解了 C/C++ 中的内存对齐概念、原因及规则。内容涵盖硬件要求、性能优化(如缓存行和 SIMD 指令)、基本数据类型与结构体的对齐规则。重点介绍了如何通过调整成员顺序优化结构体大小,以及使用编译器指令(如 pragma pack、attribute、alignas)控制对齐方式。此外,还讨论了网络传输、硬件寄存器映射等实际场景中的应用,提供了检测对齐的方法及常见问题的解决方案,旨在帮助开发者编写更高效、跨平台兼容的代码。
内存对齐(Memory Alignment)是指数据在内存中的存储地址必须是某个值的整数倍(通常是 2、4、8 等 2 的幂次方)。现代计算机体系结构对内存访问进行了优化,要求特定类型的数据必须从特定倍数的地址开始存储。
char:1 字节对齐short:2 字节对齐int/float:4 字节对齐double/long long:8 字节对齐(32 位系统可能为 4 字节)struct Example1 {
char a; // 1 字节
int b; // 4 字节(需要从 4 的倍数地址开始)
short c; // 2 字节
}; // 大小可能是 12 字节(1 + 3 填充 + 4 + 2 + 2 填充)
通过合理排列成员顺序可以节省内存:
// 优化前 (12 字节)
struct bad_layout {
char a;
int b;
short c;
};
// 优化后 (8 字节)
struct good_layout {
int b;
char a;
short c;
};
优化原则:
- 按对齐值从大到小排列成员。
- 相同类型的成员尽量集中放置。
struct S1 {
char a; // 偏移 0
// 3 字节填充
int b; // 偏移 4
short c; // 偏移 8
// 2 字节填充(使总大小为 12,是 4 的倍数)
}; // sizeof(S1) == 12
struct S2 {
int b; // 偏移 0
char a; // 偏移 4
short c; // 偏移 6
// 无填充(总大小 8,已是 4 的倍数)
}; // sizeof(S2) == 8
__attribute__((aligned(n))) 或 __attribute__((packed))// 强制 4 字节对齐
struct __attribute__((aligned(4))) aligned_struct {
char a;
int b;
};
// 取消对齐 (packed)
struct __attribute__((packed)) packed_struct {
char a;
int b;
};
__declspec(align(n))__declspec(align(16)) struct aligned_struct {
char a;
int b;
};
// 强制 16 字节对齐
struct alignas(16) AlignedStruct {
int a;
double b;
};
// 取消对齐(可能降低性能但节省空间)
#pragma pack(push, 1)
struct PackedStruct {
char a;
int b;
short c;
};
#pragma pack(pop)
alignas(16) int aligned_array[4]; // 16 字节对齐
struct alignas(8) MyStruct {
char a;
int b;
};
#if defined(_MSC_VER)
#define ALIGN(n) __declspec(align(n))
#else
#define ALIGN(n) __attribute__((aligned(n)))
#endif
ALIGN(8) struct cross_platform_struct {
int a;
char b;
};
#pragma pack(1)。#pragma pack(push, 1) // 1 字节对齐
struct network_packet {
uint16_t header;
uint32_t length;
char data[256];
};
#pragma pack(pop) // 恢复默认对齐
struct hw_register {
volatile uint32_t CTRL ALIGN(4);
volatile uint32_t STATUS ALIGN(4);
volatile uint32_t DATA ALIGN(4);
};
// 缓存行对齐 (通常 64 字节)
struct cache_line_aligned {
int data ALIGN(64);
};
// C++11
static_assert(alignof(int) == 4, "int must be 4-byte aligned");
// 运行时检查
bool is_aligned(const void* p, size_t alignment) {
return (reinterpret_cast<uintptr_t>(p) % alignment) == 0;
}
char buffer[100];
int *p = (int *)(buffer + 1); // 非对齐指针
*p = 42; // 在 ARM 上可能崩溃
解决方案:确保指针类型转换后的对齐要求。
// 方法 1:使用 memcpy
int value = 42;
memcpy(buffer + 1, &value, sizeof(value));
// 方法 2:确保对齐
int *p = (int *)(buffer + (4 - ((uintptr_t)buffer % 4)));
解决方案:重新排列成员顺序,把大对齐成员放前面。
解决方案:使用序列化代替直接内存拷贝。
解决方案:
#include <stddef.h>
printf("int alignment: %zu\n", _Alignof(int));
int is_aligned(const void *ptr, size_t alignment) {
return ((uintptr_t)ptr % alignment) == 0;
}
_Static_assert(_Alignof(double) == 8, "double must be 8-byte aligned");
示例一:
// 测试对齐访问的性能差异
void test_aligned_access() {
const int SIZE = 1000000;
// 非对齐内存
char* unaligned = new char[SIZE * 4 + 1];
int* data1 = reinterpret_cast<int*>(unaligned + 1); // 强制非对齐
// 对齐内存
int* data2 = new int[SIZE];
// 性能测试...
}
示例二:
#include <stdio.h>
#include <time.h>
#define SIZE 10000000
void test_aligned() {
_Alignas(16) int array[SIZE];
// 测试对齐访问性能...
}
void test_unaligned() {
char buffer[SIZE * 4 + 1];
int *array = (int *)(buffer + 1); // 故意不对齐
// 测试非对齐访问性能...
}
int main() {
// 对比两个函数的执行时间
}
对于多线程编程,避免 false sharing(伪共享):
struct alignas(64) CacheLineAligned {
// 典型缓存行大小 64 字节
int data;
// 填充剩余空间
};

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online