1分钟了解C语言正确使用字节对齐及#pragma pack的方法

Posted 明解嵌入式

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1分钟了解C语言正确使用字节对齐及#pragma pack的方法相关的知识,希望对你有一定的参考价值。

C语言正确使用字节对齐和#pragma pack的方式
​  C/C++编译器的缺省字节对齐方式为自然对界。即在缺省情况下,编译器为每一个变量或是数据单元按其自然对界条件分配空间。

  在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储(成员之间可能有插入的空字节),第一个成员的地址和整个结构的地址相同。

  编译器缺省的结构成员自然对界条件为“N字节对齐”,N即该成员数据类型的长度。如int型成员的自然对界条件为4字节对齐,而double类型的结构成员的自然对界条件为8字节对齐。若该成员的起始偏移不位于该成员的“默认自然对界条件”上,则在前一个节面后面添加适当个数的空字节。

        编译器缺省的结构整体的自然对界条件为:该结构所有成员中要求的最大自然对界条件。若结构体各成员长度之和不为“结构整体自然对界条件的整数倍”,则在最后一个成员后填充空字节。

        例子1(分析结构各成员的默认字节对界条界条件和结构整体的默认字节对界条件):

 struct Test
  
   char x1; // 成员x1为char型(其起始地址必须1字节对界),其偏移地址为0 
   char x2; // 成员x2为char型(其起始地址必须1字节对界,其偏移地址为1 
   float x3; // 成员x3为float型(其起始地址必须4字节对界),编译器在x2和x3之间填充了两个空字节,其偏移地址为4 
   char x4; // 成员x4为char型(其起始地址必须1字节对界),其偏移地址为8 
 ;

        在Test结构体中,最大的成员为float x3,因此结构体的自然对界条件为4字节对齐。则结构体长度就为12字节,内存布局为1100 1111 1000。

 #include <stdio.h>
 typedef struct
 
   int aa1; //4个字节对齐 1111
   char bb1;//1个字节对齐 1
   short cc1;//2个字节对齐 011
   char dd1; //1个字节对齐 1
  testlength1;
 int length1 = sizeof(testlength1); //4个字节对齐,占用字节1111 1011 1000,length = 12
 
 typedef struct
 
   char bb2;//1个字节对齐 1
   int aa2; //4个字节对齐 01111
   short cc2;//2个字节对齐 11
   char dd2; //1个字节对齐 1
  testlength2;
 int length2 = sizeof(testlength2); //4个字节对齐,占用字节1011  1111 1000,length = 12
 
 typedef struct
 
   char bb3; //1个字节对齐 1
   char dd3; //1个字节对齐 1
   int aa3; //4个字节对齐 001111
   short cc23//2个字节对齐 11
 
  testlength3;
 int length3 = sizeof(testlength3); //4个字节对齐,占用字节1100 1111 1100,length = 12
 
 typedef struct
 
   char bb4; //1个字节对齐 1
   char dd4; //1个字节对齐 1
   short cc4;//2个字节对齐 11
   int aa4; //4个字节对齐 1111
  testlength4;
 int length4 = sizeof(testlength4); //4个字节对齐,占用字节1111 1111,length = 8
 
 int main(void)
 
   printf("length1 = %d.\\n",length1);
   printf("length2 = %d.\\n",length2);
   printf("length3 = %d.\\n",length3);
   printf("length4 = %d.\\n",length4);
   return 0;
 

        改变缺省的对界条件(指定对界)
· 使用伪指令#pragma pack (n),编译器将按照n个字节对齐。
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。

        这时,对齐规则为:

        1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

        2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

        结合1、2推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

        因此,当使用伪指令#pragma pack (2)时,Test结构体的大小为8,内存布局为11 11 11 10。

        需要注意一点,当结构体中包含一个子结构体时,子结构中的成员按照#pragma pack指定的数值和子结构最大数据成员长度中,比较小的那个进行进行对齐。例子如下:

 #pragma pack(8)
 struct s1
 
   short a;
   long b;
 ;
 
 struct s2
 
   char c;
   s1 d;
   long long e;
 ;
 #pragma pack()

        sizeof(s2)的结果为24。S1的内存布局为1100 1111,S2的内存布局为1000 1100 1111 0000 1111 1111。

        例子2按照2个字节对齐时:

 #include <stdio.h>
 #pragma pack(2)
 typedef struct
 
   int aa1; //2个字节对齐 1111
   char bb1;//1个字节对齐 1
   short cc1;//2个字节对齐 011
   char dd1; //1个字节对齐 1
  testlength1;
 int length1 = sizeof(testlength1); //2个字节对齐,占用字节11 11 10 11 10,length = 10
 
 typedef struct
 
   char bb2;//1个字节对齐 1
   int aa2; //2个字节对齐 01111
   short cc2;//2个字节对齐 11
   char dd2; //1个字节对齐 1
  testlength2;
 int length2 = sizeof(testlength2); //2个字节对齐,占用字节10 11 11 11 10,length = 10
 
 typedef struct
 
   char bb3; //1个字节对齐 1
   char dd3; //1个字节对齐 1
   int aa3; //2个字节对齐 11 11
   short cc23//2个字节对齐 11
 
  testlength3;
 int length3 = sizeof(testlength3); //2个字节对齐,占用字节11 11 11 11,length = 8
 
 typedef struct
 
   char bb4; //1个字节对齐 1
   char dd4; //1个字节对齐 1
   short cc4;//2个字节对齐 11
   int aa4; //2个字节对齐 11 11
  testlength4;
 int length4 = sizeof(testlength4); //2个字节对齐,占用字节11 11 11 11,length = 8
 #pragma pack()
 int main(void)
 
   printf("length1 = %d.\\n",length1);
   printf("length2 = %d.\\n",length2);
   printf("length3 = %d.\\n",length3);
   printf("length4 = %d.\\n",length4);
   return 0;
 

        另外,还有如下的一种方式:

        · __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。

        · __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

       以上的n = 1, 2, 4, 8, 16... 第一种方式较为常见。


↓↓↓更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”↓↓↓ 

 

嵌入式开发——#pragma pack()常见用法介绍

前言

#pragma pack()是用来设置当前的对齐字节数的,C语言面试时结构体的对齐是常见考点。在嵌入式开发中,有时候是需要指定对齐字节数的,因为同样一份代码,有可能在32位的芯片里运行,也可能在64位的芯片里运行。为了保持兼容,可以用#pragma pack()来指定对齐字节,这样无论在什么环境里对齐字节数都是一样的,这样就可以避免因为对齐字节数不同导致的错误。

常见用法

#pragma pack(push)
#pragma pack(4)

此处省略n行代码

#pragma pack(pop)

解析

1.#pragma pack(push):将当前对齐字节数压栈,保存起来;
2.#pragma pack(4):指定对齐数是4字节,往后的代码都按4字节对齐;此处的4字节可以根据需求改变;
3.#pragma pack(pop):从栈里取出之前保存的对齐字节数,之后的代码还是按照以前的对齐字节数去对齐;
效果:#pragma pack(push)和#pragma pack(pop)之间的代码都会按照指定的对齐字节数对齐。

以上是关于1分钟了解C语言正确使用字节对齐及#pragma pack的方法的主要内容,如果未能解决你的问题,请参考以下文章

字节对齐#pragma pack

嵌入式开发——#pragma pack()常见用法介绍

C语言之gcc中支持的内存对齐指令

C语言 | 关于结构体内存对齐,看这篇就够了

#pragma pack(push,1)与#pragma pack的区别(转)

字节对齐 pragma