【C语言】struct与union内存对齐
条评论数据内存对齐
内存对齐的作用:
平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问只需要依次访问。
不同平台数据类型占用字节表:
数据类型 | 32位 | 64位 | 备注 |
---|---|---|---|
char | 1 | 1 | |
short | 2 | 2 | |
int | 4 | 4 | |
long | 4 | 8 | 32位于64位不同 |
float | 4 | 4 | |
char* | 4 | 8 | 其他类型指针也是如此 |
long long | 8 | 8 | |
double | 8 | 8 | |
long double | 10/12 | 10/16 | 有效字节10字节,为了内存对齐实际分配:32位12字节,64位16字节 |
结构体(struct)
结构体(struct)各成员各自拥有自己的内存,各自使用互不干涉,同时存在的,遵循内存对齐原则。
struct内存对齐规则:
- 结构体变量的起始地址能够被其最宽的成员大小整除
- 结构体每个成员相对于起始地址的偏移能够被其自身大小整除,如果不能则在前一个成员后面补充字节
- 结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节
- 程序中有
#pragma pack(n)
预编译指令,所有成员对齐以n字节为准(即偏移量是n的整数倍),不再考虑当前类型以及最大结构体类型。
实例:
//64位
struct C
{
double t; //8
char b; //1
int a; //4
short c; //2
};
t占8个字节,是b的1个字节的整数倍,不需要补充字节;
b占1个字节,不是a的4个字节的整数倍,所以b后面需要加3个补充字节;
a占4个字节,是c的2个字节的整数倍,不需要补充字节;
c占2个字节,字节数为5+1+3+4+2=18;
总字节数要为double类型所占8字节的整数倍;所以c后面增加6个补充字节。
所以sizeof(C)=24;
实例:
#pragma pack(1)
struct fun
{
int i; //4
double d; //8
char c; //1
}
因为程序中有#pragma pack(1)
预编译命令,所谓所占字节为1字节的整数倍。
总字节数为4+8+1=13
sizeof(fun)=13;
联合体(union)
联合体(union)各自成员共用一块内存空间,并且同时只有一个成员可以得到这这块内存的使用权(对该内存的读写),各变量共用一个内存首地址。
union内存对齐规则:
- 找到占用字节最多的成员
- union的字节数必须是占用字节最多的成员的的字节的倍数,而且需要能够容纳其他的成员。
实例:
typedef union
{
long i; //8
int k[5]; //4*5
char c; //1
}D;
long为占字节最多的数据类型,占8个字节;所以该union字节数为8的整数倍;成员k占字节数为4*5=20。所以该union占字节数为24个字节。
更复杂的实例:
struct BU
{
int number; //4字节
union UBffer
{
char buffer[13]; //填充3字节,该成员占16字节空间
int number;
}ubuf;
int aa; //占4字节空间,当前偏移量已补齐为20
double dou; //占8字节空间
}bu;
sizeof(BU) = 4 + 13 + 3(补齐) + 4 + 8 = 32,分析方法类似,在计算aa的偏移量时,我们可以肯定的是一定是int类型的整数倍,由于不作任何缓冲补齐的情况下,number + buffer = 17字节,为了符合规则1,需要填充3个字节。
结构体BU稍微变换下aa和dou成员顺序,则结果就大不相同:
struct BC
{
int number; //4字节
union UBffer
{
char buffer[13]; //填充7字节,该成员占20字节空间
int number;
}ubuf;
double dou; //占8字节空间,当前偏移量已补齐为24
int aa; //占4字节空间,当前占用空间36字节,最大double类型,还需要根据规则2补齐
}bu;
此时sizeof(BC) = 4 + 13 + 7(规则1补齐) + 8 + 4 + 4(规则2补齐) = 40 (8的整数倍)
__attribute__ ((__packed__))
关键字,该关键字的作用:可以让结构体,按照紧凑排列的方式,占用内存。
#include <stdio.h>
#include <iostream>
using namespace std;
struct test1
{
char c;
int i;
};
struct __attribute__ ((__packed__)) test2
{
char c;
int i;
};
int main()
{
cout << "size of test1:" << sizeof(struct test1) << endl;
cout << "size of test2:" << sizeof(struct test2) << endl;
}
运行结果:
sizeof (test1) = 8
sizeof (test2) = 5
参考:
sizeof
int a[5] = {1,2,3,4,5};
sizeof(a) = 20 //数组a占用的内存空间
sizeof(*a) = 4 //指向的int类型
char *p = "abcdefghijk";
sizeof(p) = 4 //指针占用4个字节的空间(32位机)
sizeof(*p) = 1 //指向的char类型