详解c++中的sturct

在c中struct只能存放数据,在c++中为其扩展了创建成员函数的功能,struct中的成员默认都是public的,struct的继承默认也是public,并且它是无法用于定义模板参数 ,这是它与class的主要区别。

虽然在c++中struct可以定义成员函数,不过我们还是将它主要用于定义一些 POD(plain old data),在 C++11 及之后的标准中,POD 类型需要同时满足两个独立条件:

  1. 平凡:类型具有默认的构造/拷贝/移动/析构函数(可自动生成且非虚)
  2. 标准布局(Standard Layout)​​ :内存布局与 C 兼容,成员排列顺序符合特定规则
    同时满足平凡性和标准布局的类型称为 POD 类型,这类数据可以安全使用 memcpy 等底层内存操作,因为它们的内存布局与 C 完全兼容且没有特殊处理需求。

内存对齐

内存对齐主要有三点原因,一是为了提高cpu的访问效率,cpu在读取时是按照块进行读取的,比如64位系统一般一次性读取8字节。二是早期某些平台不对齐访问会直接崩溃 或触发异常,三是为了保证原子操作,原子指令通常要求数据对齐,否则无法保证原子性。

内存对齐的基本规则:

  1. 每个成员 按照其自身大小对齐(char按1字节,int按4字节,double按8字节)
  2. 成员的起始地址必须是其对齐值的整数倍
  3. struct整体大小必须是最大对齐值的整数倍

举例:

cpp 复制代码
struct Example1 {
    char a;     // 偏移0,占用1字节
                // 填充7字节(让double从8的倍数开始)
    double b;   // 偏移8,占用8字节
    char c;     // 偏移16,占用1字节
                // 填充7字节(使总大小为8的倍数)
};  // 总大小:24字节

struct Example2 {
    double b;   // 偏移0,占用8字节
    char a;     // 偏移8,占用1字节
    char c;     // 偏移9,占用1字节
                // 填充2字节(偏移10-11)← int 需要4字节对齐
    int d;      // 偏移12,占用4字节  ← 10不是4的倍数,要从12开始
};  // 总大小:16字节 ✓

struct Example3 {    // 从大到小排列
    double b;   // 偏移0,占用8字节
    int d;      // 偏移8,占用4字节
    char a;     // 偏移12,占用1字节
    char c;     // 偏移13,占用1字节
                // 填充2字节(偏移14-15),使总大小为8的倍数
};  // 总大小:16字节

在内存对齐的情况下,我们想要访问一个数据cpu都只需要读取一次内存,假设没有内存对齐,拿Example1的成员顺序举例,内存的前8字节会存放一个完整的char a(1字节)和double b的前7字节。如果我们想要访问这个double b,cpu就需要访问两次内存才能拼凑出完整的数据。

为了提高运行效率和确保稳健性一般建议开启内存对齐,小建议:

  • 从大到小 的顺序声明成员,可以减少内存浪费(参考Example3)
    在一些嵌入式设备中,内存是比较宝贵的,内存对齐虽然可以提高运行效率,但会浪费一些内存空间,这时候我们可以通过#pragma pack来关闭内存对齐,用时间换空间。
cpp 复制代码
// 正常对齐(默认)
struct Normal {
    char a;     // 1字节
                // 填充7字节
    double b;   // 8字节
    char c;     // 1字节
                // 填充7字节
};  // 总大小:24字节

// 1字节对齐,取消填充
#pragma pack(1)
struct Packed1 {
    char a;     // 1字节
    double b;   // 8字节
    char c;     // 1字节
};  // 总大小:10字节
#pragma pack()  // 恢复默认对齐

// 2字节对齐
#pragma pack(2)
struct Packed2 {
    char a;     // 1字节
                // 填充1字节
    double b;   // 8字节(按2字节对齐,每次最多填充1字节)
    char c;     // 1字节
                // 填充1字节
};  // 总大小:12字节
#pragma pack()

// 4字节对齐
#pragma pack(4)
struct Packed4 {
    char a;     // 1字节
                // 填充3字节
    double b;   // 8字节(按4字节对齐)
    char c;     // 1字节
                // 填充3字节
};  // 总大小:16字节
#pragma pack() //恢复正常对齐

int main() {
    printf("Normal:  %zu 字节\n", sizeof(Normal));
    printf("Pack(1): %zu 字节\n", sizeof(Packed1));
    printf("Pack(2): %zu 字节\n", sizeof(Packed2));
    printf("Pack(4): %zu 字节\n", sizeof(Packed4));
    return 0;
}

//输出
Normal:  24 字节
Pack(1): 10 字节
Pack(2): 12 字节
Pack(4): 16 字节
相关推荐
小白学大数据2 小时前
分布式爬虫核心技术详解与工程实践
开发语言·分布式·爬虫·python
CoderCodingNo2 小时前
【GESP】C++一级真题 luogu-B4495, [GESP202603 一级] 交朋友
开发语言·c++
海寻山2 小时前
Java内部类:4种类型+实战场景+面试避坑
java·开发语言·面试
梦游钓鱼3 小时前
stl常用容器说明
开发语言·c++
WBluuue3 小时前
Codeforces 1088 Div1+2(ABC1C2DEF)
c++·算法
踏着七彩祥云的小丑3 小时前
Python——字符串常用操作
开发语言·python
成都易yisdong3 小时前
基于C#和WMM2025模型的地磁参数计算器实现
开发语言·c#