
🔥个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题、C/C++干货分享&学习过程记录 🍉学习方向:C/C++方向 ⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平
前言:本专栏记录了博主C++从初阶到高阶完整的学习历程,会发布一些博主学习的感悟、碰到的问题、重要的知识点,和大家一起探索C++这门程序语言的奥秘。这个专栏将记录博主C++语法、高阶数据结构、STL的学习过程,正所谓“万丈高楼平地起”嘛,我们话不多说,继续进行C++阶段的学习。本文我们不讲C++主线的内容,我们来拓展一下或者说整理一下我们学习时C/C++时经常会提到的一些专有名词,例如形参、实参,显式类型转换和隐式类型转换类型转换,内置类型、内存对齐问题等等。
C++的两个参考文档:
老朋友(非官方文档):cplusplus 官方文档(同步更新):cppreference
博主之前就介绍过内存对齐的内容,是在C语言的自定义类型:结构体里面介绍的——
【自定义类型:结构体】:类型声明、结构体变量的创建与初始化、内存对齐、传参、位段
内存对齐(Memory Alignment)是指数据在内存中的存储地址必须是某个值的整数倍(通常是2、4、8等2的幂次方)。现代计算机体系结构对内存访问进行了优化,要求特定类型的数据必须从特定倍数的地址开始存储。
(硬件要求:多数CPU访问对齐数据效率更高,有些架构(如ARM)直接不支持非对齐访问)
(性能优化:对齐数据能更好利用CPU缓存行)
(指令集要求:如SSE/AVX等SIMD指令要求数据必须对齐)
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) == 12struct 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;
// 填充剩余空间
};往期回顾:
【C/C++】具有C风格的强制类型转换:显式类型转换、隐式类型转换,C语言强制类型转换
【日常问题解决方案】VS2022不小心解决方案资源管理器把关掉了怎么办
结语:本文内容到这里就全部结束了。本文我们介绍了C语言以及C++的内存对齐问题。