C++结构体及大小计算
文章目录
- C++结构体及大小计算
- struct 和 class 区别
- 字节对齐
- 默认对齐方式
- 位域
- 使用#pragma pack(n)
- 结构体中有结构体
- Reference
struct 和 class 区别
结构体(struct)和类(class)有点像,均是定义一个数据单元,该数据单元中包含多个类型的数据,即成员变量,也可以包含多个方法。
创建结构体就像创建对象一样,一个对象有多个属性。
区别如下:
- 结构体使用关键字 struct ,类使用关键字 class;
- 通常情况下结构体声明只会声明成员变量,如果包含成员函数,就和类相似;
- 结构体声明通常不包括 public 或 private 的访问修饰符;
- 类成员默认情况是 private 的,而结构体的成员则默认为 public。
字节对齐
struct 的 sizeof 是所有成员字节对齐后长度相加,而 union 的 sizeof 是取最大的成员长度。
可以通过下面的方法来改变默认的对齐条件:
(1) 伪指令#pragma pack(n)
:C编译器将按照n个字节对齐。
(2) 伪指令#pragma pack()
:取消自定义字节对齐方式。
字节对齐的细节和编译器实现相关。
一般而言,满足3个准则:
(1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除,即 最宽成员大小 首成员大小 = 整数 \frac{最宽成员大小}{首成员大小} = 整数 首成员大小最宽成员大小=整数。
(2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是该成员大小的整数倍,即 偏移量 该成员大小 = 整数 \frac{偏移量}{该成员大小} = 整数 该成员大小偏移量=整数。如有需要编译器会在成员之间加上填充字节。
(3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节,即 结构体大小 最宽成员大小 = 整数 \frac{结构体大小}{最宽成员大小} = 整数 最宽成员大小结构体大小=整数。
基本类型是指: char、short、int、float、double 等内置数据类型。
如果一个结构体中包含另外一个结构体成员,那么此时最宽基本类型成员取最宽的基本类型。
默认对齐方式
测试机器为64位ubuntu电脑
#include <iostream>
using namespace std;
typedef struct
{
char randy1; // 1个字节 / 1字节
char randy2; // 1个字节 / 2字节
char randy3; // 1个字节 / 3字节
}Q1; // total size : 3
typedef struct
{
int num; // 4个字节
char randy; // 1个字节,补齐为8字节
}Q2; // total size : 8
typedef struct
{
char randy; // 1个字节 / 1字节
int num; // 4个字节 / 5字节 -> 补齐3字节,共8字节
}Q2_1; // total size : 8
typedef struct
{
char randy; // 1字节 / 1字节
int sesame; // 4字节 / 5字节 -> 补齐3字节,共8字节
long long qcj; // 8字节 / 16字节
char kim; // 1字节 / 17字节 -> 补齐7字节,共24字节
}Q3; // total size : 24
typedef struct
{
char randy; // 1字节 / 1字节
char kim; // 1字节 / 2字节
int sesame; // 4字节 / 6字节 -> 补齐2字节,共8字节
long long qcj; // 8字节 / 16字节
}Q3_1; // total size : 16
typedef struct
{
long sesame; // 8字节 / 8字节
char * pRandy; // 8字节 / 16字节
short int data; // 2字节 / 18字节
char randy; // 1字节 / 19字节 -> 补齐5字节
}Q4; // total size : 24
int main(void)
{
char randy;
cout << "char: " << sizeof(randy) << endl;
// 1
char* pRandy;
cout << "char *: " << sizeof(pRandy) << endl;
// 8
int sesame;
cout << "int: " << sizeof(sesame) << endl;
// 4
short int shint;
cout << "short int: " << sizeof(shint) << endl;
// 2
long qcj;
cout << "long: " << sizeof(qcj) << endl;
// 8
long long qccccccccj;
cout << "long long: " << sizeof(qccccccccj) << endl;
// 8
cout << "Q1: " << sizeof(Q1) << endl; // Q1: 3
cout << "Q2: " << sizeof(Q2) << endl; // Q2: 8
cout << "Q2_1: " << sizeof(Q2_1) << endl; // Q2_1: 8
cout << "Q3: " << sizeof(Q3) << endl; // Q3: 24
cout << "Q3_1: " << sizeof(Q3_1) << endl; // Q3_1: 16
cout << "Q4: " << sizeof(Q4) << endl; // Q4: 24
return 0;
}
位域
位域:指定某个成员变量所占用的二进制位数(Bit)
位域的宽度不能超过它所依附的数据类型的长度
如果指定了 char ch1:3,则ch1最多只能表示3个bit的char,最大不能超过8bit的char,其他基本类型依此类推。
#include <iostream>
using namespace std;
typedef struct
{
char ch1:1; // 1+3+3=7位,所以只占1个字节
char ch2:3;
char ch3:3;
}Q1; // total size : 1 Bytes
typedef struct
{
char ch1 : 5; // 5+5+3=13位,所以只占2个字节
char ch2 : 5;
char ch3 : 3;
}Q2; // total size : 2 Bytes
typedef struct
{
char ch1 : 5; // 5+5+4=14bit,占3个字节
char ch2 : 5;
char ch3 : 4;
}Q3; // total size : 3 Bytes
typedef struct
{
char ch1 : 7; // 7+7=14位,却只占2个字节 【注意】
char ch2 : 7;
}Q4; // total size : 2 Bytes
typedef struct
{
char ch1 : 1; // 偏移1个bit
int a : 16; // 偏移16个bit
int b : 16; // 偏移16个bit
}Q5; // total size : 8 Bytes
typedef struct
{
char ch1 : 1;
int a : 16;
int b : 17;
}Q6; // total size : 8 Bytes
int main(void)
{
cout << "Q1: " << sizeof(Q1) << endl; // Q1: 1
cout << "Q2: " << sizeof(Q2) << endl; // Q2: 2
cout << "Q3: " << sizeof(Q3) << endl; // Q3: 3
cout << "Q4: " << sizeof(Q4) << endl; // Q4: 2
cout << "Q5: " << sizeof(Q5) << endl; // Q5: 8
cout << "Q6: " << sizeof(Q6) << endl; // Q6: 8
return 0;
}
使用#pragma pack(n)
#include <iostream>
using namespace std;
#pragma pack(1) // 要求补齐为“1”的倍数
typedef struct
{
char randy; // 1 字节 / 1 字节,由于自定义了对齐方式,所以不再补齐
int b; // 4字节 / 5 字节
int a; // 4字节 / 9 字节
}Q1; // total size : 9 Bytes
#pragma pack(2) // 要求补齐为“2”的倍数
typedef struct
{
char randy; // 1+1 字节,由于自定义了对齐方式,补齐为"2"的倍数
int b; // 4字节 /6 字节
int a; // 4字节 /10 字节
}Q2; // total size : 10 Bytes
#pragma pack(4) // 要求补齐为“2”的倍数
typedef struct
{
char randy; // 1+1 字节,由于自定义了对齐方式,补齐为"2"的倍数
int b; // 4字节 /6 字节
short int a; // 2字节 /8 字节
int c; // 4字节 /12 字节
}Q2_1; // total size : 16 Bytes
#pragma pack(2) // 要求补齐为“2”的倍数
typedef struct
{
char randy; // 1 字节 / 2 字节 -> 补齐1字节,由于自定义了对齐方式,补齐为"2"的倍数
int b; // 4 字节 / 6 字节
int a; // 4 字节 / 10 字节
}Q3; // total size : 10 Bytes
#pragma pack(4) // 要求补齐为“4”的倍数
typedef struct
{
char randy; // 1 字节 / 4 字节 -> 补齐3字节,由于自定义了对齐方式,补齐为"2"的倍数
int b; // 4 字节 / 8 字节
int a; // 4 字节 / 12 字节
}Q3_1; // total size : 12 Bytes
#pragma pack(2) // 要求补齐为“2”的倍数
typedef struct
{
char randy; // 1 字节 / 2 字节 -> 补齐1字节,由于自定义了对齐方式,补齐为"2"的倍数
int b; // 4 字节 / 6 字节
short int c; // 2 字节 / 8 字节
}Q3_2; // total size : 8 Bytes
#pragma pack(4)
typedef struct
{
long sesame; // 8字节 / 8字节
char * pRandy; // 8字节 / 16字节
short int data; // 2字节 / 18字节
char randy; // 1字节 / 20字节 -> 补齐1字节
}Q4; // total size : 20
int main(void)
{
cout << "Q1: " << sizeof(Q1) << endl; // Q1: 9
cout << "Q2: " << sizeof(Q2) << endl; // Q2: 10
cout << "Q2_1: " << sizeof(Q2_1) << endl; // Q2_1: 10
cout << "Q3: " << sizeof(Q3) << endl; // Q3: 10
cout << "Q3_1: " << sizeof(Q3_1) << endl; // Q3_1: 12
cout << "Q3_2: " << sizeof(Q3_2) << endl; // Q3_2: 8
cout << "Q4: " << sizeof(Q4) << endl; // Q4: 20
return 0;
}
当 #pragma pack 的 n 值等于2的指数倍,如 $2^0, 2^1, 2^2, 2^3 $,若 n 超过所有数据成员长度的时候,这个 n 不产生任何效果。
结构体中有结构体
计算时仅需将内嵌的结构体展开即可
但是计算大小时,展开后的结构体的第一个成员的偏移量应当是被展开的结构体中宽度最大成员的整数倍,即 内嵌结构体最宽成员大小 首成员大小 = 整数 \frac{内嵌结构体最宽成员大小}{首成员大小} = 整数 首成员大小内嵌结构体最宽成员大小=整数
#include <iostream>
using namespace std;
typedef struct
{
short c; // 2 字节 / 2 字节
char randy; // 1 字节 / 4 字节 -> 补齐1字节
int sesame; // 4 字节 / 8 字节
int kim; // 4 字节 / 12 字节
}Q1; // total size : 12 bytes
typedef struct
{
short randy; // 2字节 / 4 字节 -> 补齐2字节, 展开后的结构体的最大成员宽度为4
struct
{
char randy; // 1 字节 / 8 字节 -> 补齐3字节
int jun; // 4 字节 / 12字节
}Q;
int kim; // 4 字节 / 16字节
}Q2; // total size : 16 bytes
int main(void)
{
cout << "Q1: " << sizeof(Q1) << endl; // Q1: 12
cout << "Q2: " << sizeof(Q2) << endl; // Q2: 16
return 0;
}
Reference
- C++中的结构体所占内存空间总结
欢迎关注公众号【三戒纪元】