C/C++内存对齐与占用内存计算

目录

 一、内存对齐

1、内存对齐的概念

2、内存对齐的原因

3、内存对齐三大原则

二、内存占用计算

1、struct结构体

2、union联合/共同体

3、enum枚举

三、一个练习


牛客刷题笔记

 一、内存对齐

1、内存对齐的概念


        现代计算机中内存空间都是按照字节byte划分的,理论上讲对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放(通过插入空字节满足规则),这就是对齐

        一般作为内存的存储介质都是能以字节为单位去访问的,尽管理论上CPU可以从内存的任意起始地址读取数据,但是对于一些CPU架构,从对齐的内存地址读取数据可能比从不对齐的内存地址读取数据要快得多。

        字节对齐是指将数据存储在内存中时,数据的起始地址与某个值(称为对齐值)的倍数对齐。对齐值通常是2的幂次方,例如1、2、4、8等。例如,如果采用4字节对齐,则将数据存储到内存中时分配到的地址总是4的倍数。值得注意的是,非常不建议使用2的幂次方以外的对齐值,因为C语言中的数据类型占据的字节大小都是2的幂次方,如char、int,使用3字节等非2的幂次方对齐值的话,内存碎片将无法避免。

        字节对齐的目的是为了提高存储器访问的效率,因为处理器在访问内存时通常会以固定大小的字(如 4 字节或 8 字节)为单位进行读写。如果内存地址不是字的边界,处理器就必须进行额外的操作来确定所需的内存位置。

        字节对齐是通过软件层面的编译器和汇编器实现的,通过指定程序执行时数据在内存中的起始地址来实现字节对齐,我们可以在C程序中使用相应的指令或在编译时更改编译器的编译选项来更改字节对齐的方式。

        在 C 语言中,可以使用 #pragma pack 指令来控制结构体、数组或其他内存块的字节对齐方式。例如,可以使用以下语句来将结构体的字节对齐方式调整为 4 字节对齐:

#pragma pack(4) //从这之后定义的结构体采用4字节对齐
struct mystruct {
   int a;    //地址偏移:0
   char b;   //地址偏移:4
   short c;  //地址偏移:8
}; //这个结构体大小为10字节(假设这个系统的short为2字节,int为4字节)
#pragma pack()  //从这之后恢复默认的对齐方式

        在这个例子中,mystruct 结构体的字节对齐方式被调整为 4 字节对齐。这意味着 mystruct 结构体的起始地址必须是 4 的倍数并且结构体中的所有字段都必须相对于起始地址对齐到 4 的倍数上

2、内存对齐的原因

  1. 平台原因(移植原因):不是所有的硬件平台都能任意访问地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:经过内存对齐后,CPU的访问效率会得到很大的提高(CPU把内存当成是一块一块的,块的大小可以是2,4,8,16Byte 大小,因此CPU在读取内存时是一块一块进行读取的,当读取块的大小是 4Byte 时,一个数据所占的字节偏移(offset)为3|4|5|6,那么CPU访问数据时便需要访问两次,才能得到完整的数据,经过内存对齐后,便可以通过一次访问CPU获取完整的数据。

3、内存对齐三大原则

  1. 数据成员对齐规则,结构体(struct)(或联合(union))的数据成员,第一个数据成员存放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员(只要该成员有子成员,比如数组、结构体等)大小的整数倍开始(如:int 在 64bit 目标平台下占用 4Byte,则要从4的整数倍地址开始存储)。
  2. 结构体作为成员,如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。
  3. 结构体的总大小,即sizeof的结果,必须是其内部最大成员长度(即前面内存对齐指令中提到的有效值)的整数倍,不足的要补齐。

二、内存占用计算

1、struct结构体

  1.  结构体的第一个成员的结构体变量在内村中存储位置是从0偏移出开始的。
  2.  从第2个成员往后,都放在一个对齐数(成员类型的大小和默认对齐数(可以通过prama pack指定)二者的最小值)的整数倍的地址处
  3. 结构体的总大小是结构体所有成员的对齐数最大的那个对齐数的整数倍  

        struct 结构占用的存储空间,不是各个属性存储空间的总和,而是最大内存占用属性的存储空间的倍数,其他属性会添加空位与之对齐。多出来的存储空间,都采用空位填充。

struct foo {
    int a;        // 4
    char pad1[4]; // 填充4字节
    char *b;      // 8
    char c;       // 1
    char pad2[7]; // 填充7字节
};
printf("%d\n", sizeof(struct foo)); // 24

2、union联合/共同体

typedef union{
    int b[5];   //20
    long long c;   //8
}DATA;

sizeof(DATA) = 24而不是20。

        关于union空间的计算,有如下几点需要注意:

  1. 大于等于最大的成员长度,此处应该是大于等于20;
  2. 满足(1)条件下最大的成员类型(union的对齐大小)的最小倍数,及长度要是大于20的前提下的8的倍数,即24;
  3. 存放变量时将会从内存中的同一位置开始,后面的变量将会覆盖以前的变量,但是如果占用的内存小于前者,那么以前未被覆盖的内存存储对象将会保持不变。
     

3、enum枚举

        enum类型的大小是由编译器根据定义值的大小来选择合适的整数类型,所以enum类型的大小并不是固定的

#include <stdio.h>


enum color1
{
    RED = 0,
    GREEN,
    BLUE
};

enum color2
{
    GRAY = 0x1122334455,
    YELLOW,
    PURPLE
};

int main(void)
{
    printf("enum color1: %d\n", sizeof(enum color1));
    printf("enum color2: %d\n", sizeof(enum color2));
    return 0;
}

在这里插入图片描述

因为enum color2的大小已经超过了4个字节,这样编译器就会把它的大小扩大。

对于计算enum的大小没有很好的办法,只能判定当给枚举中的元素赋值较小时,其类型大小为4,但超过一定值后,其大小会变为8。

三、一个练习

 int4,enum4,union14(因为pack(2)),总22

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值