结构体的对齐访问
1、结构体中元素的访问其实本质还是用指针方式,结合这个元素在整个结构体中的偏移量和这个元素的类型来进行访问的。
2、每个元素实际占的字节数和自己本身的类型所占的字节数不一定完全一样。(譬如char c实际占字节数可能是1,也可能是)
2,也可能是3,也可能是4)
3、一般来说,我们用.的方式来访问结构体元素时,我们是不用考虑结构体的元素对齐的。因为编译器会帮我们处理这个细节。
但是C语言本身是很底层的语言,而且做嵌入式开发经常需要从内存角度,以指针方式来处理结构体及其中的元素,因此还是需要掌握
结构体对齐规则。
4、结构体中元素对齐访问主要原因是为了配合硬件,也就是说硬件本身有物理上的限制,如果对齐排布和访问会提高效率,否则
会大大降低效率。
5、内存本身是一个物理器件(DDR内存芯片,SoC上的DDR控制器),本身有一定的局限性:如果内存每次访问时按照4字节对其访问,
那么效率是最高的;如果不对齐访问效率要低很多。
6、对齐访问牺牲了内存空间,换取了速度性能;而非对齐访问牺牲了访问速度性能,换取了内存空间的完全利用。
7、编译器本身可以设置内存对齐的规则,有以下的规则需要记住:
第一个:32位编译器,一般编译器默认对齐方式是4字节对齐。
8、结构体对齐的分析要点和关键:
(1)结构体对齐要考虑:结构体整体本身必须安置在4字节对齐处,结构体对齐后的大小必须是4的倍数。(编译器设置为8字节对齐时,则这里的4是8)。
(2)结构体中的每个元素本身都必须对齐存放,而每个元素本身都有自己的对齐规则。
(3)编译器考虑结构体存放时,以满足以上2点要求的最少内存需要排布来算。
举例说明:
struct mystruct1
{ //1字节对齐 4字节对齐
int a; //4 4
char b; //1 2(1+1)1个是填充的
short c; //2 2
}
分析过程:
首先是整个结构体,整个结构体变量是4字节对齐是由编译器保证的,我们不用操心。
(1)然后是第一个元素a,a的开始地址就是整个结构体的起始地址,所以自然是4字节对齐,但是a的结束地址是由下一个元素说了算。
(2)然后是第二个元素b,因为上一个元素a本身占4个字节,本身是对齐的,所以留给b的起始地址也是4字节对齐地址,所以b可以直接放
(b的位置就决定了a一共占4个字节,因为不需要填充)。
(3)b的起始地址定了以后,结束地址不能定(因为可能需要填充),结束地址要看下一个元素来定。
(4)然后就是第三个元素c,short类型的需要2个字节对齐(short类型的元素必须放在类似0,2,4,8这样的地址处,不能放在1,3这样的
奇数地址处),因此c不能紧挨着b来存放,解决方案就是b之后添加1字节的填充,然后开始存放c。c放完之后还没结束,因为整个结构体的
大小还要是4的整数倍,可能还需要填充,也可能不需要。
9、设置结构体对齐位数的指令:__attribute__((packed)) __attribute__((aligned(n)))
#pragma pack();1字节对齐 #pragma pack(4);4字节对齐
|