#pragma pack(1) 结构体字节对齐——结构体收发乱码问题复盘(一)

目录

 

背景

结构体字节对齐

当结构体为空的时,在C++中占1字节(在C中占0字节)

若结构体中所有数据类型都相同,则其所占空间为 成员数据类型长度×成员个数

结构体的大小与结构体内元素的排列顺序有很大关系

 结构体中含有结构体的情况

结构体中包含静态成员对象的情况

#pragma pack(n)的使用

指针变量类型强转

结构体字节对齐所引发的问题

感谢:https://www.cnblogs.com/longlybits/articles/2385343.html


背景

最近工作中遇到一个问题,在job1中给一个结构体中赋值,把这个结构体给发送给job2,发现job1中看的结构体内部每个值的内容,到job2中后,就都错了,排查了很长时间也不得其解,后再大神的指点下,发现是结构体字节对齐这出的错,特此记录

结构体字节对齐

当结构体为空的时,在C++中占1字节(在C中占0字节)

若结构体中所有数据类型都相同,则其所占空间为 成员数据类型长度×成员个数

结构体的大小与结构体内元素的排列顺序有很大关系

typedef struct obj1
{
    int a;
    char b;
    short c;
}S1;

//sizeof(S1) = 8

不考虑#pragma pack(n)所带来的影响,S1所占用的内存对齐方式,分析如下:首先,int类型占用4字节,char类型占用1字节,short类型占用2字节,int类型所占用的内存最大,由于结构体变量所占空间的大小必定是最宽数据类型大小的整数倍。如有需要会在最后一个成员末尾填充若干字节使得所占空间大小是最宽数据类型大小的整数倍。故存放方式为:

|--------int--------|   4字节

|char|----|--short--|   4字节

需要注意的是,对齐方式是以结构体中变量所占空间的大小为分配给其他类型变量的空间上限(换做人话说:

1.最长的是4字节,那剩下的变量所占的空间肯定超不过4字节,

2.且如果分配给其的对齐空间4字节没有用完,还有余量可以放下下一个变量,那就放,否则,就另开一个4字节,重新放新的,空余的地方那就闲在那空着,回头填充)

typedef struct obj2
{
    char a;
    int b;
    short c;
}S2;

//sizeof(S2) = 12

 放同样的几个变量,调换一下顺序,结构体所占用的空间就多了4字节,对齐方式应为:

|char|--------------|   4字节

|--------int--------|   4字节

|--short--|---------|   4字节

 结构体中含有结构体的情况

typedef struct obj2
{
    bool a;
    S1 s1;  //结构体S1充当结构体S2中的一个变量
    short b;
}S2;

//sizeof(S2) = 16

 之前S1所占的空间是8字节,可以解释的方式就是:结构体S1中最长的数据类型是int,占4个字节,结构体S1是以4字节进行对齐的,且该4字节充当了S1的数据类型所占空间长度,其他的bool也是4字节,short是2字节,对齐方式取4字节为最长,为:

|--------bool-------|   4字节

|--------int--------|   4字节  --S1

|char|----|--short--|   4字节  --S1

|--short--|---------|   4字节

总结成结论就是,结构体1里面套结构体2时,结构体1内的其他变量的数据类型长度比结构体2的内部对齐长度长的,即按照前者作为结构体1的对齐长度,否则,按后者,当然,结构体2实际占空间,还是占多数空间

再多举个例子:

typedef struct obj3
{
    bool a;
    S1 s1;
    double b;
    int c;
}S3;

//sizeof(S3)=32

这个里面,虽然S1还是以4字节对齐的,但有个double类型的变量,其数据类型所占字节为8,是结构体里面最长的,故该结构体的对齐方式如下:

|--------bool--------|    8字节

|---------s1---------|    8字节

|--------double------|    8字节

|----int----|--------|    8字节

 共计32字节。

结构体中包含静态成员对象的情况

typedef struct obj4
{
    int a;
    short b;
    static int c;
}S4;

//sizeof(S4) = 8

静态数据成员的存放位置与结构体实例的存储地址无关(注意只有在C++中结构体中才能含有静态数据成员,而C中结构体中是不允许含有静态数据成员的)。其在内存中存储方式如下:

|--------int--------|   4字节

|--short-|----|----|    4字节

 

#pragma pack(n)的使用

用法:使用了#pragma pack(n)命令强制对齐标准,则取n和结构体中最长数据类型占的字节数两者之中的小者作为对齐标准。

背景:计算机系统对基本数据类型在内存中的存放是有一定限制的,通常我们会要求这些数据的首地址的值是某个数的倍数(4或8),这就是所谓的内存对齐。

默认对齐系数:而每个平台上的编译器都有着自己的“默认对齐系数”(32位机一般为4,64位机一般为8),但我们可以通过预编译命令#pragma pack(k),k = 1,2,4,8,16来改变这个系数,其中的k就是对齐系数;

因为结构体对齐的时候是取结构体中最长数据类型与对齐系数最小的进行,且一般的数据类型长的不过也就4字节或8字节了,所以,结构体在对齐的时候,对齐系数如果没有额外设置,都会自己按上面的结构体字节对齐来显示的。

自定义取消对齐:使用#pragma pack()取消自定义字节对齐方式

 struct和union中的字节对齐通常是先局部对齐,再全局对齐。

局部对齐:数据的偏移地址是自身所占内存大小和对齐系数中较小的那个的倍数;

全局对齐:总内存是最大数据结构所占内存和对齐系数中较小的那个的倍数,union是数据可重合。

#pragma pack(show):查看当前采用的对齐方式 https://www.jianshu.com/p/d994731f658d

指针变量类型强转

背景:一般情况下,结构体的对齐方式无论是按照默认对齐或是通过命令强制对齐,除了对程序有运行速度,效率上再或者是存储空间上的的影响外,产生的问题不算大,但是,一旦牵扯到强转,那影响可就大了,就如同本次我遇到的这个问题,再一个job下构建的一个结构体,强转其指针后,作为内容转发给另一个job,另一个收到后的就是乱码了。这引出的问题还是蛮大的,而且,不好排查。

指针变量类型强转的问题比较复杂,请看后篇博客,单独复盘。

 

结构体字节对齐所引发的问题

如下博客所举出的一些例子也同样具有参考和借鉴的价值:

c语言struct结构体强制类型转换:https://blog.csdn.net/blog_xu/article/details/84374473

 

感谢:https://www.cnblogs.com/longlybits/articles/2385343.html

おすすめ

転載: blog.csdn.net/qq_17846375/article/details/114281687