结构体内存对齐
为什么要有内存对齐?
第一点:平台原因
平台原因也是移植原因:不是所有的硬件平台都能访问 任意地址上的任意数据。某些硬件平台只能在某些特殊地址处取值。
第二点:性能原因
数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因是:为了访问未对齐的内存处理器需要做两次内存访问,对齐的内存只需要一次访问。
typedef struct S
{
char ch;
int i;
}S;
int main()
{
S s;
return 0;
}
假设在32位机器下操作,那么读写操作时可以访问32位即4个字节。
![](https://i-blog.csdnimg.cn/blog_migrate/9a7911a3fff05f6b4c688aabac9c4c93.png)
因此我们得出:
结构体的内存对齐是拿 空间换 时间的做法。
如何计算结构体大小
先介绍结构体对齐的规则:
第一个成员在与结构体变量偏移量为0的地址处。
其他成员变量要对齐到对齐数的整数倍地址处。
对齐数 = min(编译器默认的对齐数,结构体成员大小)
结构体总大小为最大对齐数的整数倍。
对于嵌套结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(包括嵌套结构体的对齐数)的整数倍。
vs下的默认对齐数是8
Linux下没有默认对齐数。自身的大小就是默认对齐数
上代码:
struct A
{
char a;
int b;
char c;
};
int main()
{
printf("%d\n", sizeof(struct A));
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/c9b8eb4130d7bf8d52ba2b032b61735b.png)
交换两个成员变量:
struct A
{
char a;
char c;
int b;
};
int main()
{
printf("%d\n",sizeof(struct A));
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/a63bba42a8d1abbd6001f68d29d7c970.png)
嵌套的结构体
struct A
{
char a;
char c;
int b;
};
struct B
{
char ch;
struct A a;
int c;
};
int main()
{
printf("%d\n",sizeof(struct B));
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/05162b46d6606631fd88f4499237c834.png)
修改默认对齐数
当对齐方式不合适的时候,可以修改对齐方式
#pragma pack(4) //设置默认对齐数为4
struct A
{
char a;
char c;
int b;
};
#pragma pack() //取消设置的对齐数,还原为默认
位段
什么是位段:
位段的声明和结构是类似的,有两个不同:
1、位段的成员必须是int ,unsigned int,char 或 signed int.
2、位段的成员后边有一个冒号和一个数字。
struct S
{
int a : 3; //a占3个bit位
int b : 5; //b占5个bit位
};
int main()
{
printf("%d\n", sizeof(struct S));
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/17d74d24317b5979a45448153949b4b4.png)
这是因为一个int 类型是4个字节,而a只占3个bit位,还剩29个bit位,b又占了5个bit位,还剩下24个bit位。所以4个字节就足够了。
来看下边代码:
struct C
{
char a : 3;
char b : 6;
char c : 2;
char d : 4;
};
int main()
{
struct C c = { 0 };
c.a = 10;
c.b = 12;
c.c = 4;
c.d = 7;
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/e179db3ef82ae01025ca03d38f28a9ce.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5a705c4828340bd86f28222c389deccd.png)
位段是无可移植性的。
c标准也没规定位段的成员在内存是从左往右还是从右往左分配的。但是从上例子可以看出在vs编译器下是从右往左分配的。
反正就是:少用位段!!! 😒 除了适合位段的特定场景!!
总结:
与结构相比,位段节省了空间。但是有跨平台的问题存在。
联合体(共用体)
union关键字
联合体的特点:
联合的成员是共用同一块内存空间的,联合体的大小 至少是最大成员的大小。当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。
可以使用位段判断当前机器的大小端存储
int check_sys()
{
union Un
{
char ch;
int i;
}u;
u.i = 1;
return u.ch;
}
int main()
{
int ret = check_sys();
if (1 == ret)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/80ea6fb2833b1f981b813442a33a4b70.png)
OK!!! 💪