良许Linux教程网 干货合集 又错了,字节对齐及#pragma pack的使用

又错了,字节对齐及#pragma pack的使用

C编译器的默认字节对齐方式是自然对齐。

在默认情况下,C编译器会按照变量或数据单元的自然对齐条件来分配空间。

在结构体中,编译器会按照成员的自然对齐条件来分配空间。成员按照它们被声明的顺序在内存中顺序存储,可能会在成员之间插入空字节。第一个成员的地址与整个结构体的地址相同。

C编译器的默认结构体成员自然对齐条件为“N字节对齐”,其中N表示该成员数据类型的长度。例如,int类型的成员默认为4字节对齐,而double类型的成员默认为8字节对齐。如果成员的起始偏移不在该成员的默认自然对齐条件上,则在前一个成员后面添加适当数量的空字节。

C编译器的默认结构体整体自然对齐条件为:该结构体所有成员中要求的最大自然对齐条件。如果结构体各成员长度之和不是结构体整体自然对齐条件的整数倍,则会在最后一个成员后填充空字节。

以下是一个例子,用于分析结构体各成员的默认字节对齐条件和结构体整体的默认字节对齐条件:

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结构体中,最大的成员为flaot x3,因些此结构体的自然对界条件为4字节对齐。则结构体长度就为12字节,内存布局为1100 1111 1000。

例子2:

#include //#pragma pack(2)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 = 8int 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),C编译器将按照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。

例子:

#include #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 = 8int 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… 第一种方式较为常见。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
137e00002230ad9f26e78-265×300

137e00002230ad9f26e78-265×300

C编译器的默认字节对齐方式是自然对齐。

在默认情况下,C编译器会按照变量或数据单元的自然对齐条件来分配空间。

在结构体中,编译器会按照成员的自然对齐条件来分配空间。成员按照它们被声明的顺序在内存中顺序存储,可能会在成员之间插入空字节。第一个成员的地址与整个结构体的地址相同。

C编译器的默认结构体成员自然对齐条件为“N字节对齐”,其中N表示该成员数据类型的长度。例如,int类型的成员默认为4字节对齐,而double类型的成员默认为8字节对齐。如果成员的起始偏移不在该成员的默认自然对齐条件上,则在前一个成员后面添加适当数量的空字节。

C编译器的默认结构体整体自然对齐条件为:该结构体所有成员中要求的最大自然对齐条件。如果结构体各成员长度之和不是结构体整体自然对齐条件的整数倍,则会在最后一个成员后填充空字节。

以下是一个例子,用于分析结构体各成员的默认字节对齐条件和结构体整体的默认字节对齐条件:

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结构体中,最大的成员为flaot x3,因些此结构体的自然对界条件为4字节对齐。则结构体长度就为12字节,内存布局为1100 1111 1000。

例子2:

#include //#pragma pack(2)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 = 8int 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),C编译器将按照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。

例子:

#include #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 = 8int 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… 第一种方式较为常见。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部