概念

背景

  1. 平台原因(移植)

不同系统的对齐边界可能不一样,如Linux32位默认8字节,Linux64位默认16字节。

  1. 性能原因

硬件限制了软件设计,CPU寄存器只能从特定地址内存读取数据

原则

  1. 基础类型对齐

以sizeof大小对齐

  1. (结构体/联合体)数据成员对齐

以每个成员其sizeof大小与系统默认pack的min对齐

  1. (结构体/联合体)本身对齐

以结构体中成员最大sizeof大小与系统默认pack的min对齐

举例

  1. #pragma pack(4)
  2. struct {
  3. int i; // min(sizoef(int),4) = 4
  4. double d; // min(sizoef(double),4) = 4
  5. char c; // min(sizoef(char),4) = 1
  6. } Test; // min(max(4, 8, 1),4) = 4
  7. // 综上:4 + 8 + 4 = 16

实现

  1. 通过malloc、calloc、realloc分配的内存返回的地址都是对齐的,但对齐边界是默认的。
  2. 在Linux中,32位系统默认以8字节对齐,64位系统默认以16字节对齐。
  3. 想要动态改变对齐边界,需要使用posix_memalign、aligned_alloc、memalign、valloc、pvalloc

    posix_memalign

    1. #include <stdlib.h>
    2. int posix_memalign(void **memptr, size_t alignment, size_t size);

    测试代码

    1. void test() {
    2. char *p1, *p2, *p3, *p4, *p5;
    3. char *q1, *q2, *q3, *q4, *q5, *q6;
    4. size_t size = 17;
    5. size_t default_alignment = 16;
    6. size_t alignment = 128;
    7. // 正确写法
    8. // p1 = (char *)malloc(size);
    9. // if (p1 == NULL) {
    10. // printf("malloc error\n");
    11. // return;
    12. // }
    13. // if (posix_memalign((void **)&q2, alignment, size) != 0) {
    14. // printf("posix_memalign error\n");
    15. // return;
    16. // }
    17. // 不检查, 方便阅读
    18. p1 = (char *)malloc(size);
    19. p2 = (char *)malloc(size);
    20. p3 = (char *)malloc(size);
    21. posix_memalign((void **)&q1, alignment, size);
    22. posix_memalign((void **)&q2, alignment, size);
    23. posix_memalign((void **)&q3, alignment, size);
    24. p4 = (char *)malloc(size);
    25. p5 = (char *)malloc(size);
    26. posix_memalign((void **)&q4, alignment, size);
    27. posix_memalign((void **)&q5, alignment, size);
    28. posix_memalign((void **)&q6, alignment, size);
    29. printf("pointer\t\taddress\t\t%%default\t\t%%alignment\n");
    30. printf("p1\t\t%p\t\t%llx\t\t%llx\n", p1, (unsigned long long)p1 % default_alignment, (unsigned long long)p1 % alignment);
    31. printf("p2\t\t%p\t\t%llx\t\t%llx\n", p2, (unsigned long long)p2 % default_alignment, (unsigned long long)p2 % alignment);
    32. printf("p3\t\t%p\t\t%llx\t\t%llx\n", p3, (unsigned long long)p3 % default_alignment, (unsigned long long)p3 % alignment);
    33. printf("q1\t\t%p\t\t%llx\t\t%llx\n", q1, (unsigned long long)q1 % default_alignment, (unsigned long long)q1 % alignment);
    34. printf("q2\t\t%p\t\t%llx\t\t%llx\n", q2, (unsigned long long)q2 % default_alignment, (unsigned long long)q2 % alignment);
    35. printf("q3\t\t%p\t\t%llx\t\t%llx\n", q3, (unsigned long long)q3 % default_alignment, (unsigned long long)q3 % alignment);
    36. printf("p4\t\t%p\t\t%llx\t\t%llx\n", p4, (unsigned long long)p4 % default_alignment, (unsigned long long)p4 % alignment);
    37. printf("p5\t\t%p\t\t%llx\t\t%llx\n", p5, (unsigned long long)p5 % default_alignment, (unsigned long long)p5 % alignment);
    38. printf("q4\t\t%p\t\t%llx\t\t%llx\n", q4, (unsigned long long)q4 % default_alignment, (unsigned long long)q4 % alignment);
    39. printf("q5\t\t%p\t\t%llx\t\t%llx\n", q5, (unsigned long long)q5 % default_alignment, (unsigned long long)q5 % alignment);
    40. printf("q6\t\t%p\t\t%llx\t\t%llx\n", q6, (unsigned long long)q6 % default_alignment, (unsigned long long)q6 % alignment);
    41. free(p1);
    42. free(p2);
    43. free(p3);
    44. free(p4);
    45. free(p5);
    46. free(q1);
    47. free(q2);
    48. free(q3);
    49. free(q4);
    50. free(q5);
    51. free(q6);
    52. }

    结果分析
    image.png
    注:每次执行的结果不一样,但不影响结果之间的关系。
    在Linux 64位下运行:

  4. 分配的地址一定是alignment的整数倍

  5. 发现p3第一个被alignment整除的地址是q1,p5-q4
  6. q5-q4:隔alignment分配

疑问:

  1. 为什么q2-q1, q3-q2, q6-q5相差2*alignment,q5-q4相差aliment?
  2. 为什么p4-q3相差不是2*16?

    应用