首页 > 编程语言 >C++ 结构体对齐详解

C++ 结构体对齐详解

时间:2024-06-22 23:00:12浏览次数:12  
标签:字节 C++ char 详解 pragma InnerStruct 对齐 pack

目录

前言

一、为什么要对结构体进行对齐操作?

二、基本概念

三、 对齐规则

四、示例讲解

1.简单的变量对齐

2.结构体包含有结构体的对齐

结构体成员详细解析

五、使用指令改变对齐方式

__attribute__((packed))

#pragma pack(push, n)

#pragma pack(pop)

六、总结


前言

在C++中,结构体(struct)的对齐是指编译器为确保结构体成员在内存中的地址满足特定的对齐要求而进行的调整。对齐有助于提高访问速度,但同时也可能导致内存空间的浪费。


一、为什么要对结构体进行对齐操作?

结构体对齐的主要目的是优化内存访问速度提高内存利用率

二、基本概念

对齐(Alignment):指数据在内存中按照某种边界方式排列。例如,4字节的整数通常按4字节边界对齐。

填充(Padding):为了满足对齐要求而在数据之间插入的无意义的字节。

边界(Alignment Boundary):存储单元的起始地址必须是其大小的整数倍,例如,4字节的整数应该存储在能被4整除的地址上。

三、 对齐规则

编译器通常遵循以下规则来对齐结构体:

  • 每个成员变量的地址都是其自身大小的整数倍。
  • 结构体的总大小是其最大成员大小的整数倍。
  • 编译器可能会插入填充字节以满足上述对齐要求。

四、示例讲解

1.简单的变量对齐

struct Example {
    char a;   // 1 byte
    int b;    // 4 bytes
    short c;  // 2 bytes
};

编译器将如何对齐和填充这个结构体?假设默认对齐是4字节:

  • char a 占用1字节,开始地址0x00。
  • int b 需要4字节对齐,下一个可以被4整除的地址是0x04,因此在 a 后面插入3个填充值。
  • short c 需要2字节对齐,b 占用4个字节后,c 可以直接放在下一个地址0x08。

所以结构体在内存中的布局如下:

以结构体在内存中的布局如下:

地址内容
0x00a
0x01填充
0x02填充
0x03填充
0x04b
0x05b
0x06b
0x07b
0x08c
0x09c

总大小是10字节,但为了满足最大成员(int)的对齐要求,编译器可能会将结构体的总大小调整为12字节,使其成为4的倍数。

2.结构体包含有结构体的对齐

编译器将根据每个类型的自然对齐需求来安排各个成员的位置,以便优化内存访问。

#include <iostream>

struct InnerStruct {
    char a;   // 1字节
    int b;    // 4字节
};

struct OuterStruct {
    char c;             // 1字节
    InnerStruct inner;  // 包含内部结构体
    double d;           // 8字节
};

结构体成员详细解析

  1. 内部结构体 InnerStruct

    • char a :占用1字节。
    • int b :通常情况下,占用4字节。
  2. 外部结构体 OuterStruct

    • char c :占用1字节。
    • InnerStruct inner :包含了 InnerStruct 类型的内部结构体。
    • double d :占用8字节。
  • 内部结构体 InnerStruct 的对齐

    • char a 占用1字节。由于 int b 需要4字节对齐,所以 a 后面会有3字节的填充,使得 b 能够对齐到下一个4的倍数地址上。
    • 因此,InnerStruct 的总大小将是8字节(1字节 a + 3字节填充 + 4字节 b)。
  • 外部结构体 OuterStruct 的对齐

    • char c 占用1字节。为了对齐 InnerStructc 后面会有3字节的填充。
    • InnerStruct inner 将从4字节边界开始,总共占用8字节(如上所述)。
    • double d 通常需要8字节对齐,因此在 InnerStruct inner 后面可能会有额外的填充字节。

下面是这两个结构体在默认对齐规则下的内存布局示意图:总结20字节

OuterStruct:
| c (1B) | padding (3B) | InnerStruct (8B) | double d (8B) |

InnerStruct:
| a (1B) | padding (3B) | b (4B) |

五、使用指令改变对齐方式

可以使用编译器特定的指令或预处理指令来改变结构体的对齐方式。

__attribute__((packed))

例如,在GCC中,可以使用 __attribute__((packed)) 来消除填充:

struct ExamplePacked {
    char a;
    int b;
    short c;
} __attribute__((packed));

在MSVC中,可以使用 #pragma pack(push, n)#pragma pack(pop)

#pragma pack(push, 1)
struct ExamplePacked {
    char a;
    int b;
    short c;
};
#pragma pack(pop)

#pragma pack(push, n)

  • 功能#pragma pack(push, n) 将当前的对齐设置压入一个栈中,并将对齐值设置为 n
  • 参数
    • n:指定新的对齐值(通常是1、2、4、8或16),表示数据成员应该按照 n 字节边界进行对齐。

#pragma pack(pop)

  • 功能#pragma pack(pop) 用于从栈中弹出之前保存的对齐设置,并恢复该设置。

这样做的结果是结构体成员紧密排列,没有填充字节,但会导致性能下降,因为访问未对齐的数据可能需要更多的处理时间。

六、总结

  • 默认对齐:通常情况下,最好使用编译器的默认对齐方式,以获得最佳性能。
  • 手动对齐:只有在明确知道需要节省内存,并且能接受性能损失的情况下才使用手动对齐。

理解和控制结构体的对齐对于编写高效、可靠的C++程序非常重要。希望这篇详解能够帮助你更好地掌握结构体对齐的概念和应用。

标签:字节,C++,char,详解,pragma,InnerStruct,对齐,pack
From: https://blog.csdn.net/cl122763974/article/details/139873405

相关文章

  • 2024年华为OD机试真题-生成哈夫曼树-(C++/Java/python)-OD统一考试(C卷D卷)
    题目描述给定长度为n的无序的数字数组,每个数字代表二叉树的叶子节点的权值,数字数组的值均大于等于1。请完成一个函数,根据输入的数字数组,生成哈夫曼树,并将哈夫曼树按照中序遍历输出。为了保证输出的二叉树中序遍历结果统一,增加以下限制:二叉树节点中,左节点权值小于右节点......
  • ThreadLocal详解
    在做项目时发现项目中一般都会把用户信息存入ThreadLocal中,方便后续使用用户信息。但是ThreadLocal的原理是什么呢?这里结合网上的资料记录一下我自己的理解。ThreadLocal是什么?网上有的说法是ThreadLocal是线程本地变量,如果创建了一个ThreadLocal变量,那么访问这个变量的每......
  • 【C++进阶学习】第三弹——菱形继承和虚拟继承——菱形继承的二义性和数据冗余问题
    继承(上):【C++进阶学习】第一弹——继承(上)——探索代码复用的乐趣-CSDN博客继承(下):【C++进阶学习】第二弹——继承(下)——挖掘继承深处的奥秘-CSDN博客前言:在前面,我们已经讲过继承的相关知识,今天我们来将一个由继承拓展出来的很重要的知识,那就是——菱形继承和虚拟继承及相关知......
  • FastJson使用详解
    FastJson文章目录第一章FastJson使用详解这一篇就够了第二章FastJsonHttpMessageConverter类的作用与使用详解第三章Jackson使用详解文章目录FastJson文章目录前言一、FastJson是什么?二、使用步骤1.引入库2.序列化和反序列化Java对象3解析JSON字符串4使用注解......
  • 动态内存分配(C++)
    什么叫动态分配?动态分配的优点动态分配的语法解释动态分配的变量动态分配的数组动态分配的结构体参考什么叫动态分配?形象来说,动态分配就像是在一个大型购物广场中,你根据需要随时租用或归还一个店铺。程序运行时,如果需要更多空间来存储数据,就会向操作系统“租用”内......
  • 深入讲解C++基础知识(一)
    目录一、基本内置类型1.类型的作用2.分类3.整型3.1内存描述及查询3.2布尔类型——bool3.3字符类型——char3.4其他整型4.有符号类型和无符号类型5.浮点型6.如何选择类型7.类型转换7.1自动类型转换7.2强制类型转换7.3类型转换总结8.类型溢出8.1注意......
  • 【python】pandas:Series详解
    Series是Pandas库中的一个核心数据结构,用于处理一维数组型数据,并带有与之相关的数据标签(通常称为“索引”)。Series可以被视为一个固定大小的、有序的、可以包含任何数据类型的数组。以下是关于Series的详细介绍:定义Series是一个一维的、大小可变的、可以包含任何数据类型......
  • C++ STL容器操作:6种常用场景算法
    C++STL容器操作:6种常用场景算法    •   引言   •   概述   •   查找与计数   ▪   std::find   ▪   std::find_if   ▪   std::find_if_not   ▪   std::find_end   ▪   std::find_first_of......
  • 十大经典排序算法——插入排序与希尔排序(超详解)
    一、插入排序1.基本思想直接插入排序是一种简单的插入排序法,基本思想是:把待排序的记录按其数值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。2.直接插入排序当插入第end+1 个元素时,前面的arr[0],arr[1],... ,arr[end]已经......
  • 【C语言/C++干货系列】你真的了解数组吗?
    目录广告前言一维数组二维数组字符数组尾声广告                      点击......