我正在使用几个在线编译器来测试示例程序(供参考,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中的解释之后询问,我没有抓到它。)
答案 0 :(得分:4)
对齐属性描述为here in the GNU Compiler documentation。
它说的是:
此属性指定变量或结构字段的最小对齐方式,以字节为单位。
这意味着您将设置最小对齐,但数组的开头。保证数组的其余部分是连续的,这意味着后面的地址取决于示例uint8_t
中的类型。如果数组起始地址与0x2aff9175b0a0
类似,那么下一个地址必须是0x2aff9175b0a1
(0xa0 --> 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字节边界上对齐,Array2
在Array1
之后的8字节边界上。同样,Array3
和Array4
位于适当对齐的边界上。 (当然,32字节边界是以十六进制结束z0
的地址,其中z
是偶数.8字节边界是以8
结尾的地址或十六进制0
。)
u16_a1
到u16_a3
数组没有对齐约束。它们各为6个字节,三个地址相差6个字节。
相比之下,u16_a4
到u16_a6
数组被限制为32字节边界。正如您所看到的,它们的地址彼此相差32个字节,每个地址位于32位边界,而如果它们不受约束,则它们会更加接近。您可能会注意到u16_a3
和u16_a4
碰巧在记忆中是连续的 - 纯粹的机会,而不是我的计划。
您可以调整此代码以使用其他类型和路线。