概念
背景
- 平台原因(移植)
不同系统的对齐边界可能不一样,如Linux32位默认8字节,Linux64位默认16字节。
- 性能原因
原则
- 基础类型对齐
以sizeof大小对齐
- (结构体/联合体)数据成员对齐
以每个成员其sizeof大小与系统默认pack的min对齐
- (结构体/联合体)本身对齐
以结构体中成员最大sizeof大小与系统默认pack的min对齐
举例
#pragma pack(4)
struct {
int i; // min(sizoef(int),4) = 4
double d; // min(sizoef(double),4) = 4
char c; // min(sizoef(char),4) = 1
} Test; // min(max(4, 8, 1),4) = 4
// 综上:4 + 8 + 4 = 16
实现
- 通过malloc、calloc、realloc分配的内存返回的地址都是对齐的,但对齐边界是默认的。
- 在Linux中,32位系统默认以8字节对齐,64位系统默认以16字节对齐。
想要动态改变对齐边界,需要使用posix_memalign、aligned_alloc、memalign、valloc、pvalloc
posix_memalign
#include <stdlib.h>
int posix_memalign(void **memptr, size_t alignment, size_t size);
测试代码
void test() {
char *p1, *p2, *p3, *p4, *p5;
char *q1, *q2, *q3, *q4, *q5, *q6;
size_t size = 17;
size_t default_alignment = 16;
size_t alignment = 128;
// 正确写法
// p1 = (char *)malloc(size);
// if (p1 == NULL) {
// printf("malloc error\n");
// return;
// }
// if (posix_memalign((void **)&q2, alignment, size) != 0) {
// printf("posix_memalign error\n");
// return;
// }
// 不检查, 方便阅读
p1 = (char *)malloc(size);
p2 = (char *)malloc(size);
p3 = (char *)malloc(size);
posix_memalign((void **)&q1, alignment, size);
posix_memalign((void **)&q2, alignment, size);
posix_memalign((void **)&q3, alignment, size);
p4 = (char *)malloc(size);
p5 = (char *)malloc(size);
posix_memalign((void **)&q4, alignment, size);
posix_memalign((void **)&q5, alignment, size);
posix_memalign((void **)&q6, alignment, size);
printf("pointer\t\taddress\t\t%%default\t\t%%alignment\n");
printf("p1\t\t%p\t\t%llx\t\t%llx\n", p1, (unsigned long long)p1 % default_alignment, (unsigned long long)p1 % alignment);
printf("p2\t\t%p\t\t%llx\t\t%llx\n", p2, (unsigned long long)p2 % default_alignment, (unsigned long long)p2 % alignment);
printf("p3\t\t%p\t\t%llx\t\t%llx\n", p3, (unsigned long long)p3 % default_alignment, (unsigned long long)p3 % alignment);
printf("q1\t\t%p\t\t%llx\t\t%llx\n", q1, (unsigned long long)q1 % default_alignment, (unsigned long long)q1 % alignment);
printf("q2\t\t%p\t\t%llx\t\t%llx\n", q2, (unsigned long long)q2 % default_alignment, (unsigned long long)q2 % alignment);
printf("q3\t\t%p\t\t%llx\t\t%llx\n", q3, (unsigned long long)q3 % default_alignment, (unsigned long long)q3 % alignment);
printf("p4\t\t%p\t\t%llx\t\t%llx\n", p4, (unsigned long long)p4 % default_alignment, (unsigned long long)p4 % alignment);
printf("p5\t\t%p\t\t%llx\t\t%llx\n", p5, (unsigned long long)p5 % default_alignment, (unsigned long long)p5 % alignment);
printf("q4\t\t%p\t\t%llx\t\t%llx\n", q4, (unsigned long long)q4 % default_alignment, (unsigned long long)q4 % alignment);
printf("q5\t\t%p\t\t%llx\t\t%llx\n", q5, (unsigned long long)q5 % default_alignment, (unsigned long long)q5 % alignment);
printf("q6\t\t%p\t\t%llx\t\t%llx\n", q6, (unsigned long long)q6 % default_alignment, (unsigned long long)q6 % alignment);
free(p1);
free(p2);
free(p3);
free(p4);
free(p5);
free(q1);
free(q2);
free(q3);
free(q4);
free(q5);
free(q6);
}
结果分析
注:每次执行的结果不一样,但不影响结果之间的关系。
在Linux 64位下运行:分配的地址一定是alignment的整数倍
- 发现p3第一个被alignment整除的地址是q1,p5-q4
- q5-q4:隔alignment分配
疑问: