结构体大小计算与位域大小计算
本博文纯属个人理解,有错误请多指正。
首先,各个变量所占的字节:
类型 | 字节(64位) | 字节(32位) |
---|---|---|
char | 1 | 1 |
unsigned char | 1 | 1 |
short | 2 | 2 |
unsigned short | 2 | 2 |
int | 4 | 4 |
unsigned int | 4 | 4 |
long | 8 | 4 |
unsigned long | 8 | 4 |
float | 4 | 4 |
double | 8 | 8 |
指针 | 8 | 4 |
结构体计算大小原则
原则一、结构体中元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每一个元素放置到内存中时,它都会认为内存是以它自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始(以结构体变量首地址为0计算)。
原则二、经过原则一之后,检查计算出的存储单元是否为所有元素中最宽的元素的长度的整数倍,是,则结束;若不是,则补齐为它的整数倍。
结构体位域大小计算原则
1.如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2.如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3.如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
4.如果位域字段之间穿插着非位域字段,则不进行压缩;
5. 整个结构体的总大小为最宽基本类型成员大小的整数倍。
本人对理论理解不是特别彻底,直接看例子对我来说更容易一点。
例子:
//由于存在对齐规则,a对b对齐,b为4个字节,所以a占4个字节,4+4+8=16
struct X {
char a; //1字节
int b; //4
double c; //8
} S1;
//1.同理,a对齐b,8+8+4=20,由于结构体计算大小原则二,结构体大小应为所有元素中最宽的元素长度的整数倍
//2.b是所有元素最宽的长度(8),结构体大小为24
struct X {
char a; //1
double b; //8
int c; //4
} S2;
//8+4+4=16
struct X {
double a; //8
char b; //1
int c; //4
} S3;
//8+4+4+4+4=24
struct X {
double a; //8
char b; //1
int c; //4
char d; //1
int e; //4
} S5;
//8+8+4+4+1=25,补齐之后s6=32
struct X {
int e; //4
double a; //8
char b; //1
int c; //4
char d; //1
} S6;
//4+4+8=16
struct X {
char a; //1
int b; //4
double c; //8
};
//8+16=24,a的大小应为X中元素最宽元素double c的倍数而不是16的倍数
struct Y {
char a; //8
X b; //16
};
//4+1=5
struct tag0 {
int a; //4
char b; //1
} __attribute((packed)); //编译器选项,不填充
//4+4=8
struct tag1 {
int a;
int b;
} __attribute((packed));
//4+4+8=16
struct tag2 {
int a;
int b;
char *c; //指针,64位下占8个字节
} __attribute((packed));
//4+4+0=8
struct tag3 {
int a;
int b;
char c[0]; //占位符,大小为0
} __attribute((packed));
//4+4+1=9
struct tag4 {
int a;
int b;
char c[1]; //字符数组,只有一个元素,占用1个字节
} __attribute((packed));
//字太多分两行看
//1.char占1个字节8位,由于b1只占用前5位,但是后三位不够存放b2(b2要占5位,剩余的3位不够用,根据位域大小计算原则2需要用新的存储单元来放b2)
//2.所以b2存放在新的存储空间,以此类推,8+8+8+8+8=5个字节
typedef struct AA {
unsigned char b1 : 5; //位域,char占1个字节8位,5代表b1占用前5位,后3位不用
unsigned char b2 : 5;
unsigned char b3 : 5;
unsigned char b4 : 5;
unsigned char b5 : 5;
} AA;
//1.int占4个字节32位,由于b1只占用5位(0~4),根据位域大小计算原则1,b2紧临b1存储(5~9),以此类推,5+5+5+5+5=25
//2.32位只用了25位(相当于b1~b5存在了前25位,不到4个字节)根据位域大小计算原则5,结构体的总大小应为最宽成员int(4个字节)的整数倍
//3.所以结构体的大小为4个字节
typedef struct BB {
unsigned int b1 : 5; //位域,int占4个字节32位,5代表b1占用前5位
unsigned int b2 : 5;
unsigned int b3 : 5;
unsigned int b4 : 5;
unsigned int b5 : 5;
} BB;
//1.int占4个字节32位,b1占用1位(0),空出两位(1~2),b3占用3位(3~5),b4占用2位(6~7),b5占用3位(8~10)
//2.b5遇到类型不同的short b6,b6需要存在新的存储单元,补齐之后b1和b3~b5占4个字节,b6占用4位(0~3),
//3.b6遇到类型不同的int b7,b7需要存在新的存储单元,补齐之后b6占4个字节(short占4个字节32位)
//4.b7占用1位(0),补齐之后b7占用4个字节,所以结构体大小为4+4+4=12个字节
typedef struct CC {
int b1:1;
int :2; //空出2位,啥也没存
int b3:3; //b1~b5为同类型的变量,所以可以存储在连续的空间中,总共占4个字节
int b4:2;
int b5:3;
short b6:4; //b6和上面的类型不同,所以需要存储在新的单元中,总共占4个字节
int b7:1; //b7和上面的b6类型不同,所以需要存储在新的单元中,总共占4个字节
}CC;
//1.int占4个字节32位,a占用29位,剩余的3位不够放b的29位,所以b存放在新的单元中,剩余的3位不够放c的4位
//2.所以b存放在新的单元中,所以4+4+4=12
struct test3{
int a:29;
int b:29;
int c:4;
};
//a占用27位,剩余的5位不够b存放,所以b存放在新的单元中,c占用4位放在b之后,所以4+4=8个字节
struct test4{
int a:27;
int b:27;
int c:4;
};
以上。
参考:
https://blog.csdn.net/liukun321/article/details/6974282
https://blog.csdn.net/xy010902100449/article/details/50752573