字节对齐的原因
为了提高 CPU 的存储速度,编译器会对 struct 和 union的存储进行优化,即进行字节对齐。
对齐方式
对于 struct 或 union 中的 struct 或者 union 来说,它们的字节对齐标准就是它的所有成员中字节数最大的数据的字节数。
一般情况下 C/C++ 的变量所占用的字节数
char: 1字节;
short: 2字节;
int: 4字节;
long: 4字节;
long long: 8字节;
float: 4字节;
double: 8字节;
bool: 1字节;
*struct 中字节对齐需要满足的条件:
1、某个变量存放的起始位置相对于结构的起始位置的偏移量是该变量字节数的整数倍
2、结构所占的总字节数是该结构中字节数最长的变量的整数倍
struct Struct
{
double d1;
char d2;
int d3;
}a;
sizeof(a) = 8 + 1 + 3 + 4 = 16。其中补上的 3 个字节是为了让 int 型数据的起始位置相对于结构起始位置的偏移量为 4 的整数倍。
struct Struct
{
char d1;
double d2;
int d3;
}b;
sizeof(b) = 1 + 7 + 8 + 4 = 20。 20 / 8 = 2 …… 4,所以需要再补上 4 个字节,使之成为 8 的 整数倍
*union 中字节对齐需要满足的两个条件:
1) union的大小必须足够容纳字节数最大的变量
2)union的大小需要能够被其所包括的基础成员类型的大小所整除
字节对齐的另一种方式
VC提供了 #pragma pack(n) 用来自定义字节对齐方式
有一下两种情况:
1、n 大于变量的字节数:偏移量只满足默认的字节对齐方式;
2、n 小于变量所占的字节数:偏移量是 n 的整数倍,不使用默认的字节对齐方式。
#pragma pack(push) // 保持对齐状态
#pragma pack(4) // 设定为 4 字节对齐
struct test
{
char m1;
double m2;
int m3;
}a;
#pragma pack(pop) // 恢复对齐状态
sizeof(a) = 1 + 3 + 8 + 4 = 16 // 其中补上三位是因为 n 小于 8,所以 m2 的起始位置相对于结构起始位置的偏移量是 n,即为 4.
#pragma pack(8)
struct S1
{
char a;
long b;
};
struct S2
{
char c;
struct S1 d;
long long e;
};
#pragma pack()
sizeof(S1) = 1 + 3 + 4 = 8
sizeof(S2) = 1 + 3 + 8 + 4 + 8 = 24。// 其中加上的 4 是因为变量 e 的字节数是 8 ,其相对与起始位置的偏移量必须是 8 的倍数。
字节对齐问题的讨论到上边已经结束了。下面再加上我碰到的两道题目作为实例:
#include <stdio.h>
union
{
char x[5];
int i;
}a;
int main()
{
a.x[0] = 10;
a.x[1] = 1;
printf("%d\n", a.i);
printf("%d\n", sizeof(a));
return 0;
}
输出为 266 8
解析:
对 union 分配内存涉及字节对齐问题,在上方已有详细描述,在此只简单解释一番。union 分配的内存必须是 union 中所有基本数据类型的倍数。
在此题中即为 1 和 4 的倍数,又 char x[5] 占用 5 个字节,故 union 分配的内存大小应为 8 个字节。
windows 系统中高字节在后,低字节在前。而 char x[5] 只有前两个元素有值,即两个值只占 2 个字节,也即 union 中的 int 型数据中只有低两位上有值。
即 i 的二进制表示为:
00000000 00000000 00000001 00001010
即: 2^1 + 2^3 + 2^8 = 266
也相当于十六进制 0x010A, 即: 10 * 16^0 + 1 * 16^2 = 266