结构体对齐方式__attribute__和 #pragma pack()(基于32位编译器)

一、理解
    1.1 编译器默认对齐方式
        多少位的编译器就默认多少位对齐,譬如:32位的编译器,默认4字节对齐。
        求证方法很简单,自己写一个结构体,存放几个不同类型的元素,打印出结构体大小即可。
    1.2 对齐分析(基于4字节对齐)
        (1)结构体起始地址必须在4字节对齐处,对齐后的大小为4的倍数;
        (2)每个元素根据自身的对齐规则,对齐存放,参考:三、应用->3.1默认对齐方式;

        
二、方法
    2.1 #pragma pack()与#pragma pack(n)
        (1)解释
            #pragma pack( ):更改为编译器默认对齐方式;
            #pragma pack(n):更改为n字节对齐。
        (2)用法
            用于改变一个区间内结构体的对齐方式;
            在区间头部使用#pragma pack(n),末尾使用#pragma pack();
            我也试过不在区间末尾加#pragma pack();,程序也可以运行,但保险起见,还是加上。
    2.2 __attribute__((packed))与__attribute__((aligned(n)))
        (1)解释
            __attribute__((packed)):更改为1字节对齐;
            __attribute__((aligned(n))):更改为n字节对齐。
            注意:下划线和括号都是两个,一个都不能少!
        (2)用法    
            两个都是更改单个结构体的对齐方式;
            放在结构体定义末尾处即可。
        
三、应用
    3.1 默认对齐方式
        (1)特例          

			#include <stdio.h>

			struct MyStruct1       //1字节            4字节
			{
				char a ;//             1              1
				char b;//              1              1
				short c;//             2              2
				int  d ;//             4              4	
			};

			int main(void)
			{
			
				printf("Mystruct1 = %d.\n",sizeof(struct MyStruct1));

				return 0;
			}

            输出结果:          

                MyStruct1 = 8;
            受变量类型本身的对齐规则的影响,1字节、4字节对齐,结构体占用空间大小相同            
        (2)一般情况

            #include <stdio.h>

            struct MyStruct1       //1字节            4字节
            {
                char a ;//             1              4(1+3)
                int  d ;//             4              4    
                char b;//              1              1
                short c;//             2              3(2+1)
                
            };

            int main(void)
            {
                printf("Mystruct1 = %d.\n",sizeof(struct MyStruct1));

                return 0;
            }

            输出结果:         
                MyStruct1 = 12;


    3.2 #pragma pack()
            参考默认对齐方式,这里不再赘述。
    
    3.3 #pragma pack(n)

			#include <stdio.h>

			#pragma pack(8);
			 //__attribute__(aligned(8))  __attribute__((packed))

			struct MyStruct1       //1字节            4字节                8字节 
			{
				char c_a ;//             1              4(1+3)              4(1+3)
				int  b ;  //             4              4                   4
				short s_c;//             2              4(2+2)              8(2+6)
			};
			 
			struct MyStruct2
			{
				char c_d;//               1              4(1+3)             4(1+3)
				int  i_e;//               4              4                  4
				struct MyStruct1 S1;//    7              12                 16
			};

			#pragma pack();


			int main(void)
			{
				printf("MyStruct1 = %d.\n",sizeof(struct MyStruct1));
				printf("MyStruct2 = %d.\n",sizeof(struct MyStruct2));

				// printf("MyStruct11 = %d.\n",sizeof(struct MyStruct11));
				// printf("MyStruct21 = %d.\n",sizeof(struct MyStruct21));
				return 0;
			}

        输出结果:
                    MyStruct1 = 12.
                    MyStruct2 = 20.        
        思考一下,MyStruct2为什么是28呢,double的对齐规则是怎样呢?
    
    3.4 __attribute__((packed))      

			#include <stdio.h>

			struct MyStruct1         //1字节          4字节             
			{
				char c_a ;//             1            4(1+3)
				int  i_b ;//             4            4
				short s_c;//             2            4(2+2)
			}; 
			struct MyStruct2
			{
				char c_d;//               1            4(1+3)             
				int  i_e;//               4            4             
				struct MyStruct1 S1;//    7            12              
			};


			struct MyStruct11
			{
				char c_a1 ;//             1              
				int  i_b1 ;//             4             
				short s_c1;//             2             
			}__attribute__((packed));  
			struct MyStruct21
			{
				char c_d1;//               1         
				int  i_e1;//               4         
				struct MyStruct11 S11;//   7         
			}__attribute__((packed));   

			int main(void)
			{
				printf("MyStruct1 = %d.\n",sizeof(struct MyStruct1));
				printf("MyStruct2 = %d.\n",sizeof(struct MyStruct2));

				printf("MyStruct11 = %d.\n",sizeof(struct MyStruct11));
				printf("MyStruct21 = %d.\n",sizeof(struct MyStruct21));
				return 0;
			}

        输出结果:  
                    MyStruct1 = 12.
                    MyStruct2 = 20.        
                    MyStruct11 = 7.
                    MyStruct21 = 12.

    
    3.5 __attribute__((aligned(n)))

        使用__attribute__((aligned(n)))测试1、2、8、16字节对齐

			#include <stdio.h>

			// #pragma pack(8);
			 //__attribute__((aligned(8)))  __attribute__((packed))

			struct MyStruct1       //1字节            4字节                8字节                16字节
			{
				char c_a ;//             1              4(1+3)              
				int  i_b ;//             4              4                   
				short s_c;//             2              4(2+2)              
			}; 
			struct MyStruct2
			{
				char c_d;//               1              4(1+3)            
				int  i_e;//               4              4                 
				struct MyStruct1 S1;//    7              12                 
				double d_f;//             8              8                  
			};


			struct MyStruct11
			{
				char c_a1 ;//             1              4(1+3)              4(1+3)              4(1+3)
				int  i_b1 ;//             4              4                   4                   4(1+3)
				short s_c1;//             2              4(2+2)	             8(2+6)              8(2+6)
			}__attribute__((aligned(1)));  
			struct MyStruct21
			{
				char c_d1;//               1              4(1+3)             4(1+3)              4(1+3)
				int  i_e1;//               4              4                  4(1+3)              4(1+3)
				struct MyStruct11 S11;//   7              12                 16                  16
				double d_f1; //            8              8                  8                   16(8+8)
			}__attribute__((aligned(1)));   

			// #pragma pack();


			int main(void)
			{
				printf("MyStruct1 = %d.\n",sizeof(struct MyStruct1));
				printf("MyStruct2 = %d.\n",sizeof(struct MyStruct2));

				printf("MyStruct11 = %d.\n",sizeof(struct MyStruct11));
				printf("MyStruct21 = %d.\n",sizeof(struct MyStruct21));
				return 0;
			}

                输出结果:
                MyStruct1 = 12.
                MyStruct2 = 28.        
                MyStruct11 = 12.
                MyStruct21 = 28.
            更改为1字节并没有用,这里打印结构体MyStruct11、MyStruct21各元素的地址:
                MyStruct1 = 12.
                MyStruct2 = 28.
                MyStruct11.c_a1 = 0x804a020.
                MyStruct11.i_b1 = 0x804a024.
                MyStruct11.s_c1 = 0x804a028.
                MyStruct11 = 12.
                MyStruct21.c_d1 = 0x804a02c.
                MyStruct21.i_e1 = 0x804a030.
                MyStruct21.S11 = 0x804a034.
                MyStruct21.d_f1 = 0x804a040.
                MyStruct21 = 28.
            下面测试一下2字节对齐:
            输出结果:
                MyStruct1 = 12.
                MyStruct2 = 28.
                MyStruct11.c_a1 = 0x804a020.
                MyStruct11.i_b1 = 0x804a024.
                MyStruct11.s_c1 = 0x804a028.
                MyStruct11 = 12.
                MyStruct21.c_d1 = 0x804a02c.
                MyStruct21.i_e1 = 0x804a030.
                MyStruct21.S11 = 0x804a034.
                MyStruct21.d_f1 = 0x804a040.
                MyStruct21 = 28.
            下面测试一下8字节对齐:
            输出结果:
                MyStruct1 = 12.
                MyStruct2 = 28.
                MyStruct11.c_a1 = 0x804a040.
                MyStruct11.i_b1 = 0x804a044.
                MyStruct11.s_c1 = 0x804a048.
                MyStruct11 = 16.
                MyStruct21.c_d1 = 0x804a060.
                MyStruct21.i_e1 = 0x804a064.
                MyStruct21.S11 = 0x804a068.
                MyStruct21.d_f1 = 0x804a078.
                MyStruct21 = 32.
            下面测试一下16字节对齐:
            输出结果:
                MyStruct1 = 12.
                MyStruct2 = 28.
                MyStruct11.c_a1 = 0x804a040.
                MyStruct11.i_b1 = 0x804a044.
                MyStruct11.s_c1 = 0x804a048.
                MyStruct11 = 16.
                MyStruct21.c_d1 = 0x804a060.
                MyStruct21.i_e1 = 0x804a064.
                MyStruct21.S11 = 0x804a070.
                MyStruct21.d_f1 = 0x804a080.
                MyStruct21 = 48.
            分析:使用 __attribute__((aligned(n)))更改结构体对齐数时:
            (1)无法更改1、2字节(≤4)的对齐数,改完后还是默认的4字节对齐;
            (2)更改为8、16字节(>4)的对齐数,完全按照字节对齐分布,以16字节对齐为例:
                MyStruct11.c_a1 为char型,剩下字节不够存放i_b1,实际为(1+3)字节;
                MyStruct11.i_b1 为int型,占用4字节;
                MyStruct11.s_c1 为short型,占用2字节,后面没有元素了,实际占用为(2+6)字节。
                所以16字节对齐时,MyStruct11大小为:4+4+8 = 16字节。
                MyStruct21.c_d1为char型,占用1字节,剩下字节不够存放i_e1,实际为(1+3)字节;
                MyStruct21.i_e1为int型,占用4字节,剩下字节不够存放S11,实际为(4+8)字节;
                MyStruct21.S11占用16字节,刚好占满;
                MyStruct21.d_f1 为double类型,占用8字节,后面没有元素,实际占用为(8+8)字节。
                所以16字节对齐时,MyStruct21占用大小为:4+12+16+16 = 48字节。
                
                
                
    分析就到这里,就不再深入了,前面留了#pragma pack(8)对齐方式,就不再赘述了。
    我也百度了很多资料,都没有详细讲是何种原因造成两种对齐方式的异常现象。于是自己测试了一下,也可能和编译器优化,不同的系统有关系。能力有限,没有深入研究对齐方式异常的原因,这篇博客主要提供一个解决对齐问题的思路,并对对齐方式做个详细记录。如有大佬提供思路,愿闻其详。
         

  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值