关于结构体的几个问题
内存分配
要求:
1. 边界要求,即每个成员存放的起始地址必须是成员类型大小的整数倍。
2. 结构体的起始存储地址必须是边界要求最严格的数据类型所要求的位置。
3. 结构体的末尾地址也必须是边界要求最严格的数据类型所要求的位置。
例子:
struct List {
char a;
int b;
long long c;
char d;
};
此结构体内存分配:
1. 按第2个要求,这个结构体的起始存储地址必须是8的倍数(long long 类型占8个字节);
2. 接下来, 变量a占一个字节;
3. 按第1个要求,b必须存放在4的倍数的地址,所以变量a后补3个字节,再存放b;
4. 然后,起始地址恰好是8的倍数,存放变量c,占8个字节(若其实地址不是8的倍数,补足字节后再存放);
5. 变量d占一个字节;
6. 按第3个要求,必须再补足7个字节。
所以,这个结构体一共占用的内存是1 + (3)+ 4 + 8 + 1 +(7) = 24个字节。
从这个例子可以看到,一共分配了24个字节,可实际使用的却只有14个,使用率不高。
重新排列成员列表,让边界要求最严格的成员首先出现,边界要求最弱的成员最后出现。
struct List {
long long c;
int b;
char a;
char d;
};
重新排列后,结构体占用的内存为 8 + 4 + 1 + 1 + (2)=16个字节,实际使用14个字节。
结论:同样一个结构体,按第2种成员排列方式,占用内存减少,使用率提高。
但缺点是实际工程中,代码可读性可能降低。
位段
位段的声明和普通结构体的声明相同,但有两个例外:
1. 位段成员必须声明为int, signed int 和 unsigned int类型。
2. 成员名后面是一个冒号和一个整数,整数指定该位段所占用位的数目。
例子:
struct Node {
unsigned ch : 7
unsigned font : 6
unsigned size : 19
};
优点:
1. 可以把长度为奇数的数据包装在一起,节省空间。
2. 可以访问一个整型值的部分内容。