首页 > 其他分享 >1分钟了解C语言正确使用字节对齐及#pragma pack的方法

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

时间:2023-05-05 21:14:25浏览次数:37  
标签:11 字节 int C语言 char 对界 pragma 对齐 pack

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

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

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

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

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

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

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

 1 #include <stdio.h>
 2 typedef struct
 3 {
 4   int aa1; //4个字节对齐 1111
 5   char bb1;//1个字节对齐 1
 6   short cc1;//2个字节对齐 011
 7   char dd1; //1个字节对齐 1
 8 } testlength1;
 9 int length1 = sizeof(testlength1); //4个字节对齐,占用字节1111 1011 1000,length = 12
10 
11 typedef struct
12 {
13   char bb2;//1个字节对齐 1
14   int aa2; //4个字节对齐 01111
15   short cc2;//2个字节对齐 11
16   char dd2; //1个字节对齐 1
17 } testlength2;
18 int length2 = sizeof(testlength2); //4个字节对齐,占用字节1011  1111 1000,length = 12
19 
20 typedef struct
21 {
22   char bb3; //1个字节对齐 1
23   char dd3; //1个字节对齐 1
24   int aa3; //4个字节对齐 001111
25   short cc23//2个字节对齐 11
26 
27 } testlength3;
28 int length3 = sizeof(testlength3); //4个字节对齐,占用字节1100 1111 1100,length = 12
29 
30 typedef struct
31 {
32   char bb4; //1个字节对齐 1
33   char dd4; //1个字节对齐 1
34   short cc4;//2个字节对齐 11
35   int aa4; //4个字节对齐 1111
36 } testlength4;
37 int length4 = sizeof(testlength4); //4个字节对齐,占用字节1111 1111,length = 8
38 
39 int main(void)
40 {
41   printf("length1 = %d.\n",length1);
42   printf("length2 = %d.\n",length2);
43   printf("length3 = %d.\n",length3);
44   printf("length4 = %d.\n",length4);
45   return 0;
46 }

        改变缺省的对界条件(指定对界)
· 使用伪指令#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指定的数值和子结构最大数据成员长度中,比较小的那个进行进行对齐。例子如下:

 1 #pragma pack(8)
 2 struct s1
 3 {
 4   short a;
 5   long b;
 6 };
 7 
 8 struct s2
 9 {
10   char c;
11   s1 d;
12   long long e;
13 };
14 #pragma pack()

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

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

 1 #include <stdio.h>
 2 #pragma pack(2)
 3 typedef struct
 4 {
 5   int aa1; //2个字节对齐 1111
 6   char bb1;//1个字节对齐 1
 7   short cc1;//2个字节对齐 011
 8   char dd1; //1个字节对齐 1
 9 } testlength1;
10 int length1 = sizeof(testlength1); //2个字节对齐,占用字节11 11 10 11 10,length = 10
11 
12 typedef struct
13 {
14   char bb2;//1个字节对齐 1
15   int aa2; //2个字节对齐 01111
16   short cc2;//2个字节对齐 11
17   char dd2; //1个字节对齐 1
18 } testlength2;
19 int length2 = sizeof(testlength2); //2个字节对齐,占用字节10 11 11 11 10,length = 10
20 
21 typedef struct
22 {
23   char bb3; //1个字节对齐 1
24   char dd3; //1个字节对齐 1
25   int aa3; //2个字节对齐 11 11
26   short cc23//2个字节对齐 11
27 
28 } testlength3;
29 int length3 = sizeof(testlength3); //2个字节对齐,占用字节11 11 11 11,length = 8
30 
31 typedef struct
32 {
33   char bb4; //1个字节对齐 1
34   char dd4; //1个字节对齐 1
35   short cc4;//2个字节对齐 11
36   int aa4; //2个字节对齐 11 11
37 } testlength4;
38 int length4 = sizeof(testlength4); //2个字节对齐,占用字节11 11 11 11,length = 8
39 #pragma pack()
40 int main(void)
41 {
42   printf("length1 = %d.\n",length1);
43   printf("length2 = %d.\n",length2);
44   printf("length3 = %d.\n",length3);
45   printf("length4 = %d.\n",length4);
46   return 0;
47 }

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

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

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

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


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

 

标签:11,字节,int,C语言,char,对界,pragma,对齐,pack
From: https://www.cnblogs.com/Sharemaker/p/17375352.html

相关文章

  • Webpack 面试题
    一、Webpack如何解决跨域问题?(三种方式)来源1.使用代理:服务端是别人的,使用这个方式devServer:{proxy:{//重写的方式,把请求代理到express服务器上'/api':{target:'http://localhost:3000',pathRewrite:{'/api':''}//若请求路径为/......
  • C语言中的内存管理
    C语言中定义了四个内存区间:https://mp.weixin.qq.com/s/MtwQrp752qLMwDAFrBYm0w代码区;全局变量和静态变量区;局部变量区即栈区;动态存储区即堆区。1>栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。2>堆区(heap) —一般由......
  • C语言从入门到精通
    1.C语言较为底层,更接近硬件,效率较高,因此更合适用于开发操作系统;2.C语言支持函数操作,但它并不属于函数式编程。函数式编程可以理解为高级的函数操作,例如,函数的嵌套定义、匿名函数、闭包、惰性求值等等,但C语言中,基本上只能常规的定义函数、调用函数;常量、变量和关键字数据类......
  • C语言指针说明
    地址 说到指针,先说说地址,看一段小程序#include"stdio.h"intmain(){  inta=10;  int*p=&a;  printf("%p\n",p);  return0; }//output0x7fff8b6a378c"0x7fff8b6a378c"是系统RAM中的特定位置,通常以十六进制的数字表示,系统通过这个地址,就可......
  • C语言文件操作详解
    C语言中没有输入输出语句,所有的输入输出功能都用ANSIC提供的一组标准库函数来实现。文件操作标准库函数有:文件的打开操作fopen打开一个文件文件的关闭操作fclose关闭一个文件文件的读写操作fgetc从文件中读取一个字符......
  • C语言中 p三种用法的区别
    请看下面三种定义:constchar*p;charconst*p;char*constp;首先看第一种,我们先看p,本着”从里往外”的原则,p是一个char*类型的变量,但char*前面有一个const修饰,即p所指向的内容为const类型不可修改,我们可以写如下程序进行实验,当试图对p指向的数组的第一个元素进行修改时,......
  • C语言结构体--位域
    有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。比如开关只有通电和断电两种状态,用0和1表示足以,也就是用一个二进位。正是基于这种考虑,C语言又提供了一种叫做位域的数据结构。在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),这......
  • C语言多线程
    线程按照其调度者可以分为用户级线程和核心级线程两种 用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持; 我们常用基本就是用户级线程,所以就只总结一下POSIX提供的用户级线程接口; 基本线程操作相关的函数: 1......
  • C语言文件操作
    函数原型size_t fread(void *buffer, size_t size, size_t count, FILE *stream);  size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream);  功能 fread和fwrite用于读写记录,这里的记录是指一串固定长度的字节,比如一个......
  • C语言--指针的进阶2
    函数指针存放函数地址的指针。intAdd(intx,inty){ returnx+y;}intmain(){ inta=10; intb=20; //printf("%d\n",Add(a,b));//&函数名,函数名,都是函数的地址。 //int(*pa)(int,int)=&Add; int(*pa)(int,int)=Add;//对于函数指针的解引用下面都......