__attribute(对齐)测试。它有什么作用?

时间:2017-06-21 06:20:43

标签: c

我正在使用几个在线编译器来测试示例程序(供参考,ideone中的一个和tutorialspoint中的一个) 该计划是:

#include <stdio.h>
#include <stdint.h> 

uint8_t Array[5]__attribute((aligned(32)));
uint8_t Array2[5]__attribute((aligned(8)));
static uint8_t Array3[5]__attribute((aligned(8)));
static uint8_t Array4[5]__attribute((section("bbs"),aligned(32)));


int main()
{
    printf("%p %p %p %p %p \n", &Array[0], &Array[1], &Array[2], &Array[3],&Array[4]);
    printf("%p %p %p %p %p \n", &Array2[0], &Array2[1], &Array2[2], &Array2[3],&Array2[4]);
    printf("%p %p %p %p %p \n", &Array3[0], &Array3[1], &Array3[2], &Array3[3],&Array3[4]);
    printf("%p %p %p %p %p \n", &Array4[0], &Array4[1], &Array4[2], &Array4[3],&Array4[4]);

    return 0;
}

结果是例如

0x2aff9175b0a0 0x2aff9175b0a1 0x2aff9175b0a2 0x2aff9175b0a3 0x2aff9175b0a4 
0x2aff9175b080 0x2aff9175b081 0x2aff9175b082 0x2aff9175b083 0x2aff9175b084 
0x2aff9175b068 0x2aff9175b069 0x2aff9175b06a 0x2aff9175b06b 0x2aff9175b06c 
0x2aff9175b040 0x2aff9175b041 0x2aff9175b042 0x2aff9175b043 0x2aff9175b044 

我可以看到对齐似乎对数组元素的存储位置没有影响。对齐实际上做了什么? (我在阅读here中的解释之后询问,我没有抓到它。)

2 个答案:

答案 0 :(得分:4)

对齐属性描述为here in the GNU Compiler documentation

它说的是:

  

此属性指定变量或结构字段的最小对齐方式,以字节为单位。

这意味着您将设置最小对齐,但数组的开头。保证数组的其余部分是连续的,这意味着后面的地址取决于示例uint8_t中的类型。如果数组起始地址与0x2aff9175b0a0类似,那么下一个地址必须是0x2aff9175b0a10xa0 --> 0xa1)之后的1个字节。

解释你的例子:

Alignment 32 beginning at: 0x2aff9175b0a0 --> 0xa0 = 160 = 32 * 5
Alignment 8  beginning at: 0x2aff9175b080 --> 0x80 = 128 = 8  * 16
Alignment 8  beginning at: 0x2aff9175b068 --> 0x68 = 104 = 8  * 13
Alignment 32 beginning at: 0x2aff9175b040 --> 0x40 = 64  = 32 * 2

如您所见,数组开头的对齐方式符合您的要求。您可以增加对齐并再次查看地址。但也要考虑:

  

请注意,对齐属性的有效性可能会受到链接器固有限制的限制。在许多系统上,链接器只能安排变量对齐到某个最大对齐。

此外,如果您使用printf()打印%p的地址,则还会将地址转换为void*,因为指针类型不需要具有相同的地址表示除了其中一些之外。这个事实也是explained here

答案 1 :(得分:2)

您可以通过稍微不同的示例代码更好地了解正在发生的事情。这是您的计划的改编。打印由[1]等索引的元素的地址没有任何优点;这些位置100%由类型(uint8_t)和数组的起始地址控制。

#include <stdio.h>
#include <stdint.h> 

uint8_t Array1[5]__attribute((aligned(32)));
uint8_t Array2[5]__attribute((aligned(8)));
static uint8_t Array3[5]__attribute((aligned(8)));
static uint8_t Array4[5]__attribute((aligned(32)));

uint16_t u16_a1[3];
uint16_t u16_a2[3];
uint16_t u16_a3[3];

uint16_t u16_a4[3] __attribute((aligned(32)));
uint16_t u16_a5[3] __attribute((aligned(32)));
uint16_t u16_a6[3] __attribute((aligned(32)));

int main(void)
{
    printf("%s: %p\n", "Array1", (void *)Array1);
    printf("%s: %p\n", "Array2", (void *)Array2);
    printf("%s: %p\n", "Array3", (void *)Array3);
    printf("%s: %p\n", "Array4", (void *)Array4);

    printf("%s: %p\n", "u16_a1", (void *)u16_a1);
    printf("%s: %p\n", "u16_a2", (void *)u16_a2);
    printf("%s: %p\n", "u16_a3", (void *)u16_a3);
    printf("%s: %p\n", "u16_a4", (void *)u16_a4);
    printf("%s: %p\n", "u16_a5", (void *)u16_a5);
    printf("%s: %p\n", "u16_a6", (void *)u16_a6);

    return 0;
}

我不得不删除section(bbs),指令; Mac链接器不接受它。我还将Array重命名为Array1,以便与其他数组名称保持一致。

我得到的输出是:

Array1: 0x10a1fc040
Array2: 0x10a1fc048
Array3: 0x10a1fc028
Array4: 0x10a1fc020
u16_a1: 0x10a1fc04e
u16_a2: 0x10a1fc054
u16_a3: 0x10a1fc05a
u16_a4: 0x10a1fc060
u16_a5: 0x10a1fc080
u16_a6: 0x10a1fc0a0

请注意,Array1的地址在32字节边界上对齐,Array2Array1之后的8字节边界上。同样,Array3Array4位于适当对齐的边界上。 (当然,32字节边界是以十六进制结束z0的地址,其中z是偶数.8字节边界是以8结尾的地址或十六进制0。)

u16_a1u16_a3数组没有对齐约束。它们各为6个字节,三个地址相差6个字节。

相比之下,u16_a4u16_a6数组被限制为32字节边界。正如您所看到的,它们的地址彼此相差32个字节,每个地址位于32位边界,而如果它们不受约束,则它们会更加接近。您可能会注意到u16_a3u16_a4碰巧在记忆中是连续的 - 纯粹的机会,而不是我的计划。

您可以调整此代码以使用其他类型和路线。