结构体对齐粗暴理解

C、C++数据类型所占字节数

32位编译器


char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int:  4个字节
unsigned int : 4个字节
float:  4个字节
double:   8个字节
long:   4个字节
long long:  8个字节
unsigned long:  4个字节

64位编译器

char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int:  4个字节
unsigned int : 4个字节
float:  4个字节
double:   8个字节
long:   8个字节
long long:  8个字节

unsigned long:  8个字节

结构体对齐:

公式1:前面的地址必须是后面的地址正数倍,不是就补齐

公式2:整个Struct的地址必须是最大字节的整数倍

练习: Struct E1 { int a;char b; char c}e1;

第一地址肯定存放a是4Byte地址,

第二地址,b要1Byte的地址,

来欢迎公式一登场: 4 == 1*N (N等于正整数) 答"是"!地址现在为5Byte,下一个c要1Byte的地址同上,所以,就是6Byte。

来欢迎公式二登场,在这个E1中最大的字节是4,而我们的地址字节是6,4的整数倍不是6,所以,要加2Byte(总地址),So,整个字节为8!

CAUTION: 每个特定平台上的编译器都有自己的默认“对齐系数”。可以通过预编译命令#pragma pack(n)

对齐系数:

重要规则:
1,复杂类型中各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个类型的地址相同;
2,每个成员分别对齐,即每个成员按自己的方式对齐,并最小化长度;规则就是每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数中较小的一个对齐;
3,结构、联合或者类的数据成员,第一个放在偏移为0的地方;以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度两个中比较小的那个进行;也就是说,当#pragma pack指定的值等于或者超过所有数据成员长度的时候,这个指定值的大小将不产生任何效果;
4,复杂类型(如结构)整体的对齐是按照结构体中长度最大的数据成员和#pragma pack指定值之间较小的那个值进行;这样在成员是复杂类型时,可以最小化长度;
5,结构整体长度的计算必须取所用过的所有对齐参数的整数倍,不够补空字节;也就是取所用过的所有对齐参数中最大的那个值的整数倍,因为对齐参数都是2的n次方;这样在处理数组时可以保证每一项都边界对齐;


更改c编译器的缺省字节对齐方式:
在缺省情况下,c编译器为每一个变量或数据单元按其自然对界条件分配空间;一般地可以通过下面的两种方法来改变缺省的对界条件:
方法一:
使用#pragma pack(n),指定c编译器按照n个字节对齐;
使用#pragma pack(),取消自定义字节对齐方式。

方法二:
__attribute(aligned(n)),让所作用的数据成员对齐在n字节的自然边界上;如果结构中有成员的长度大于n,则按照最大成员的长度来对齐;
__attribute((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

综上所述,下面给出例子并详细分析:

例子一:
#pragma pack(4)
class TestB
{
public:
int aa; //第一个成员,放在[0,3]偏移的位置,
  char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
  short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
  char c; //第四个,自身长为1,放在[8]的位置。
};
可见,此类实际占用的内存空间是9个字节。根据规则5,结构整体的对齐是min( sizeof( int ), pack_value ) = 4,所以sizeof( TestB ) = 12;


例子二:
#pragma pack(2)
class TestB
{
public:
int aa; //第一个成员,放在[0,3]偏移的位置,
  char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
  short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
  char c; //第四个,自身长为1,放在[8]的位置。
};
可见结果与例子一相同,各个成员的位置没有改变,但是此时结构整体的对齐是min( sizeof( int ), pack_value ) = 2,所以sizeof( TestB ) = 10;


例子三:
#pragma pack(4)
class TestC
{
public:
char a; //第一个成员,放在[0]偏移的位置,
  short b; //第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。
  char c; //第三个,自身长为1,放在[4]的位置。
};
整个类的实际内存消耗是5个字节,整体按照min( sizeof( short ), 4 ) = 2对齐,所以结果是sizeof( TestC ) = 6;


例子四:
struct Test
{
char x1; //第一个成员,放在[0]位置,
short x2; //第二个成员,自身长度为2,按2字节对齐,所以放在偏移[2,3]的位置,
float x3; //第三个成员,自身长度为4,按4字节对齐,所以放在偏移[4,7]的位置,
char x4; //第四个陈冠,自身长度为1,按1字节对齐,所以放在偏移[8]的位置,
};
所以整个结构体的实际内存消耗是9个字节,但考虑到结构整体的对齐是4个字节,所以整个结构占用的空间是12个字节。


例子五:
#pragma pack(8)

struct s1
{
short a; //第一个,放在[0,1]位置,
long b; //第二个,自身长度为4,按min(4, 8) = 4对齐,所以放在[4,7]位置
};
所以结构体的实际内存消耗是8个字节,结构体的对齐是min( sizeof( long ), pack_value ) = 4字节,所以整个结构占用的空间是8个字节。

struct s2
{
char c; //第一个,放在[0]位置,
s1 d; //第二个,根据规则四,对齐是min( 4, pack_value ) = 4字节,所以放在[4,11]位置,
long long e; //第三个,自身长度为8字节,所以按8字节对齐,所以放在[16,23]位置,
};
所以实际内存消耗是24自己,整体对齐方式是8字节,所以整个结构占用的空间是24字节。

#pragma pack()
所以:
sizeof(s2) = 24, s2的c后面是空了3个字节接着是d。

原文地址:https://www.cnblogs.com/xyptechnology/p/7764967.html