union 共用体/联合体
- 结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
- 结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
结构体字节对齐
其实字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:
- 数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第⼀个数据成员放在offset为0的地⽅,以后每个数据成员存储的起始位置要从该成员⼤⼩或者成员的⼦成员⼤⼩(只要该成员有⼦成员,⽐如说是数组,结构体等)的整数倍开始(⽐如int为4字节,则要从4的整数倍地址开始存储。 min(当前开始的位置m n)
- 结构体作为成员:如果⼀个结构⾥有某些结构体成员,则结构体成员要从其内部最⼤元素⼤⼩的整数倍地址开始存储.(struct a⾥存有struct b,b⾥有char,int ,double等元素,那b应该从8的整数倍开始存储.)
- 收尾⼯作:结构体的总⼤⼩,也就是sizeof的结果,.必须是其内部最⼤成员的整数倍.不⾜的要补⻬。
struct P1 {
BOOL sex; // 2
NSString * name1; // 8
int age; // 4
};// 2 + (6 第二个成员8个字节,起始位置从8的整数倍开始开始 ) + 8 + 4 = 20 -> 8(最大成员的)的整数倍 24
struct P2{
int age; // 4
NSString * name1; // 8
BOOL sex; // 2
};// 4 + (4 第二个成员8个字节,起始位置从8的整数倍开始开始 ) + 8 + 2 = 18 -> 8(最大成员的)的整数倍 24
struct P3{
BOOL sex; // 2
int age; // 4
NSString * name1; // 8
};// 2 + (2) + 4 + 8 = 16 -> 16
struct P1 p1 ;
struct P2 p2 ;
struct P3 p3 ;
NSLog(@"%ld %ld %ld", sizeof(p1), sizeof(p2), sizeof(p3));
// 输出 24 24 16
struct T1{
int a; // 8
char b;
struct P1 p;// 24 最大成员8
};// 4 + 2 + (2) + 24 = 32 -> 32
struct T2{
char b;
struct P1 p;// 24 最大成员8
};// 2 + (6 P1 的最大成员是8 起始从8的倍数开始) + 24 = 32 -> 32
struct T3{
int a; // 8
char b;
struct P4 p4; // 8 最大成员是4
};// 4 + 2 + (2) + 8 = 16 -> 16 最大成员是4 4的倍数
struct T4{
char b;
struct P4 p4; // 8 最大成员是4
};// 2 + (2) + 8 = 12 -> 最大成员是4,所以是4的倍数。12
结构体位域
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。比如一个Bool类型,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
- 位段成员必须声明为int、unsigned int或signed int类型(short char long)。
- 位域可以无名,只用来填充不能访问
// 位域的格式
// 类型说明符 位域名:位域长度
struct Person {
int a1:2;
int a2:5;
int a3:7;
int b1: 8;
int b2:4;
int c1:5;
int c2: 6;
int c3: 3;
int c4: 2;
};
(lldb) p/t t
(Test) $0 = {
p = (a1 = 0b01, a2 = 0b00001, a3 = 0b0000001, b1 = 0b00000001, b2 = 0b0001, c1 = 0b00001, c2 = 0b000001, c3 = 0b001, c4 = 0b01)
b = 0b000000000000000000000010010 0000100000100010000000100000010000101
//在int 类型的 4字节长度(定义的类型)是一个整体,如果存不下将会从下一个字节开始存。位的的长度不能超多 4 * 8 的长度
struct P1 {
char c1: 4;
char c2: 5;
char c3: 6;
char c4: 7;
};
(lldb) p/t t
p1 = (c1 = 0b0001, c2 = 0b00001, c3 = 0b000001, c4 = 0b0000001)
b = 0b00000001000000010000000100000001
// 此时char 1个字节是一个整体。
struct P2 {
char c1: 5;
int c2: 6;
char c3: 6;
char c4: 7;
};
(lldb) p/t t
p2 = (c1 = 0b00001, c2 = 0b000001, c3 = 0b000001, c4 = 0b0000001)
b = 0b0000000000000000000000000000000000000001000000010000000000100001
// 如果类型是不同的,当前存储室,当前的类型是一个整体,是否可以存放在后续的位置之内,
// 比如 c2 是4个字节。从c1到4个字节(32位)剩余的位能够存储,则在后续存储。
// 对于c3 char是1个字节。从c3 到当前的剩余1个字节整体不能存下c3.所以向后移动存在下一个字节(8位)中
位域存储的大致规则
- 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
- 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
- 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
- 如果位域字段之间穿插着非位域字段,则不进行压缩;
- 整个结构体的总大小为最宽基本类型成员大小的整数倍。