嵌入式技术论坛
直播中

邓世金

7年用户 42经验值
擅长:制造/封装 模拟技术 连接器 光电显示 EDA/IC设计 接口/总线/驱动
私信 关注
[经验]

C语言-结构体对齐详解

`
C语言-结构体对齐详解
朱有鹏
1、结构体为何要对齐访问
访问结构体元素时需要对齐访问,主要是为了配合硬件,也就是说硬件本身有物理上的限制,因为对齐排布和访问可以提高访问效率。
如:
struct s
{
char c;
int b;
};

内存本身是一个物理器件(DDR内存芯片,SoC上的DDR控制器),本身有一定的局限性:如果内存每次访问时按照4字节对齐访问,那么效率是最高的;如果你不对齐访问效率要低很多。
还有很多别的因素和原因,导致我们需要对齐访问。譬如Cache的一些缓存特性,还有其它硬件(譬如MMU、LCD显示器)的一些内存依赖特性,所以会要求内存对齐访问。对比对齐访问和不对齐访问,似然对齐访问牺牲了内存空间,换取了速度性能,而非对齐访问则是牺牲了访问速度,换取了内存空间的利用率。
2、结构体对齐的规则和运算
(1)结构体对齐实例分析
编译器本身可以设置内存对齐的规则,有以下的规则需要记住:
第一个:32位编译器,一般编译器默认对齐方式是4字节对齐。
#include
struct mystruct1 {                        // 1字节对齐         4字节对齐
                int a;                                        // 4                  4
                char b;                                        // 1                  2
                short c;                                        // 2                  2
};
int main(void){
                // 4字节对齐时
                printf("sizeof (struct mystruct1) = %d.
", sizeof (struct mystruct1));     
                // sizeof (struct mystruct1) = 8
}

分析:整个结构体变量4字节对齐是由编译器保证的,我们不用操心。第一个元素a的第一个字节的地址就是整个结构体的起始地址,所以自然是4字节对齐的。但是a的结束地址要由下一个元素说了算。然后是第二个元素b,因为上一个元素a本身占4字节,本身就是对齐的。所以留给b的开始地址也是4字节对齐地址的,所以b可以直接放,b放的位置就决定了a一共占4字节,因为不需要填充。b的起始地址定了后,结束地址不能定(因为可能需要填充),结束地址要由一个元素来定。然后是第三个元素c,short类型需要2字节对齐,short类型元素必须放在类似0,2,4,8这样的地址处,不能放在1,3这样的奇数地址处,因此c不能紧挨着b来存放,解决方案是在b之后添加1字节的填充(padding),然后再开始放c。c放完之后还没结束,当整个结构体的所有元素都对齐存放后,还没结束,因为整个结构体大小还要是4的整数倍。
(2)结构体对齐总结
第一:当编译器将结构体设置为4字节对齐时,结构体整体必须从4字节对齐处存放,结构体对齐后的大小必须4的倍数,如果编译器设置为8字节对齐,则这里的4就8。
第二:结构体中每个元素本身都也必须对齐存放。
第三:编译器在考虑以上两点情况下,实现以最少内存来开辟结构体空间。
3手动对齐
如果编译器自动实现结构体对齐,我们就称为自动对齐,与之相反,使用#pragma进行对齐的就是手动对齐。
#pragma备用告诉编译器,程序员自己希望的对齐方式。比如,虽然编译器的默认对齐方式是4,但是如果我们不希望按照4对齐,而是希望实现别的对齐方式,譬如希望1字节对齐,也可能希望是8,甚至可能希望128字节对齐,这个时候就必须使用#pragma进行手动对齐了。
常用的设置手动对齐的命令有两种:第一种是#pragma pack(),这种就是设置编译器1字节对齐,不过也可以认为是设置为不对齐或者取消对齐;第二种是#pragma pack(4),这个括号中的数字表示希望以多少字节进行对齐。
我们需要#prgama pack(n)开头,以#pragma pack()结尾,定义一个区间,这个区间内的对齐参数就是n。
include
#pragma pack(4)     // 4字节对齐
struct mystruct1
{
            int a;
            char b;
            short c;
};
struct mystruct2
{
            char a;
            int b;
            short c;
};
typedef struct myStruct5
{
            int a;
            struct mystruct1 s1;       
            double b;
            int c;
}MyS5;
struct stu
{
                char sex;
                int length;
                char name[10];
};
#pragma pack()
int main(void)
{
                printf("sizeof(struct mystruct1) = %d.
", sizeof(struct mystruct1));
                printf("sizeof(struct mystruct2) = %d.
", sizeof(struct mystruct2));
                printf("sizeof(struct mystruct5) = %d.
", sizeof(MyS5));
                printf("sizeof(struct stu) = %d.
", sizeof(struct stu));
        return 0;
分析:本例中四个结构体对齐结果:
(1).struct mystruct1:
        1字节对齐                        2字节对齐                        4字节对齐                        8字节对齐
        4                                4                   4                   4
        1                                2(1+1)               2(1+1)              2(1+1)
        2                                2                    2                  2
(2). sizeof(struct mystruct2):
        1字节对齐                        2字节对齐                        4字节对齐                        8字节对齐
        1                                2(1+1)               4(1+3)              4(1+3)        4                                4                   4                   4
2                                2                   4(2+2)               4(2+2)
(4). MyS5(struct myStruct5):
        1字节对齐                        2字节对齐                        4字节对齐                        8字节对齐
        4                                4                   4                   4
        7                                8                   8                   8
        8                                8                   8                   8
        4                                4                   4                   4
(4). struct stu:
        1字节对齐                        2字节对齐                        4字节对齐                        8字节对齐
        1                                2(1+1)                           4(1+3)               4(1+3)
        4                                4                                4                    4
        10                                10                                12(10+2)             12(10+2)
对于#prgma pack的手动对齐来说,在很多C环境下都是支持的,自然gcc也支持,如果不是有哦特殊需求的话,不建议使用。

`

回帖(2)

邓世金

2017-7-12 16:49:04
举报

邓世金

2017-7-12 16:49:23
朱老师物联网讲堂1群 397164505
朱有鹏单片机学习1群 214959925
举报

更多回帖

发帖
×
20
完善资料,
赚取积分