首页 > 系统相关 >C++内存对齐

C++内存对齐

时间:2023-04-09 15:45:30浏览次数:60  
标签:字节 int C++ 内存 pragma 对齐 pack

0x1 什么是内存对齐,为什么需要它?

尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的.它一般会以双字节,4字节,8字节,16字节甚至32字节为单位来存取内存,这些存取单位称为内存存取粒度

现在考虑4字节存取粒度的处理器取int类型变量(32位系统),该处理器只能从地址为4的倍数的内存开始读取数据。

假如没有内存对齐机制,数据可以任意存放,现在一个int变量存放在从地址1开始的连续四个字节地址中,该处理器去取数据时,要先从0地址开始读取第一个4字节块,剔除不想要的字节(0地址),然后从地址4开始读取下一个4字节块,同样剔除不要的数据(5,6,7地址),最后留下的两块数据合并放入寄存器.这需要做很多工作。

0x2 内存对齐规则

  • 结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。

  • 结构体的总大小为有效对齐值的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

    C++11后可以使用alignof获取一个结构体类型的有效对齐值

#include <iostream>

struct Info {
    uint8_t x1;
    uint32_t x2;
    uint64_t x3;
};

int main(int argc, char *argv[]) {
    std::cout << alignof(Info) << std::endl;// 输出8
    std::cout << sizeof(Info) << std::endl;// 输出16
    return 0;
}

/*
这里因为Info的有效对齐值为8,那么x2的对齐值是min(sizeof(uint32_t), 8) = 4,即其offset为4的整数倍,
x3的对齐值为8,其offset为8的整数倍。
所有Info的大小为 4 + 4 + 8 = 16
*/

如果调换x2和x3的顺序,看看会发生什么...

#include <iostream>

struct Info {
    uint8_t x1;
    uint64_t x3;
    uint32_t x2;
};

int main(int argc, char *argv[]) {
    std::cout << alignof(Info) << std::endl;// 输出8
    std::cout << sizeof(Info) << std::endl;// 输出24
    return 0;
}

/*
这里因为Info的有效对齐值为8,x3的offset需要为8的整数倍,会导致Info对象的字节数变大
所有Info的大小为 8 + 8 + 8 = 24
*/

0x3 改变对齐规则

  • alignas加在结构体前,如果小于有效对齐值会被忽略(只能加大对齐值)

    struct alignas(8) S {};
    struct alignas(1) U { S s; }; // 错误:如果没有 alignas(1) 那么 U 的对齐将会是 8
    
  • 使用#pragma pack

#pragma pack (n)         // 作用:C编译器将按照n个字节对齐。
#pragma pack ()          // 作用:取消自定义字节对齐方式。

#pragma pack (push,n)   // 作用:是指把原来对齐方式设置压栈,并设新的对齐方式设置为n个字节对齐
#pragma pack(pop)        // 作用:恢复对齐状态

举个例子

#pragma pack (1)   // 设置对齐方式

struct A
{
    int a;
    char b;
    short c;
    double d;
};

#pragma pack ()   // 取消自定义对齐方式

#pragma pack (push, 1)  // 设置对齐方式

struct B
{
    int a;
    char b;
    short c;
    double d;
};

#pragma pack (pop)   // 恢复对齐方式

/* 
这里sizeof(A) = 15,sizeof(B) = 15,因为它们的对齐值都为1
*/

标签:字节,int,C++,内存,pragma,对齐,pack
From: https://www.cnblogs.com/chelseafan/p/17300402.html

相关文章

  • 内存函数
    今日份学习“内存函数”前言:当我们了解字符串相关函数可以对一系列的字符串进行操作,但这些函数只针对于字符串操作且太局限性了,所以为了破开这个局限性,该篇文章将介绍的函数就能解决这个问题。我们知道字符串相关的函数肯定是对字符串的每个字符进行操作控制的,每个字符且只占一个字......
  • Android遇到内存泄漏和性能优化,需要采取以下措施
    内存泄漏:a.使用内存分析工具,如AndroidStudio的MemoryProfiler或LeakCanary,找出内存泄漏的位置,并修复代码。b.避免在Activity或Fragment中使用静态变量或单例模式,因为它们可能会持有对Activity或Fragment的引用,并导致内存泄漏。c.及时释放不再使用的资源,如关闭文件、释放......
  • C++伪随机数
    直接上代码吧用的是vs2019#include<iostream>usingnamespacestd;intmain(){ //系统生成随机数//rand()%100生成0~99 srand(time(NULL));//随机数种子,不加这行下一行就是伪随机数 intrandom_num=rand()%100+1;//1-100; //cout<<random_nu......
  • C++ map注意事项
    std::map<int,std::string>map;判断key是否存在时不能使用:std::stringstr=map[9];  //这样不存在时会新增!!!需要这样判断:std::map<int,std::string>::interatoriter;iter=map.find(9);if(iter!=map.end())  //存在else//不存在......
  • C++逆向分析——对象拷贝
    对象拷贝我们通常存储对象,都用数组、列表之类的来存储,那如下所示我们使用数组来存储对象,但是在工作中发现这个数组不够用了,就需要一个更大的数据,但我们重新创建一个数组还需要把原来的数据复制过来;在C语言中可以使用函数来进行拷贝,直接拷贝内存,在C++中实际上跟C语言要做的事情是......
  • C++逆向分析——友元、内部类、命名空间和static
    友元友元可以理解为:朋友、元素;老师认为这个友元是C++中的一个垃圾,因为友元的存在破坏了面向对象的封装性,不推荐使用,之所以有这个章节是因为有人不了解这个概念。注意:在一些新版本的C++编译器里面已经不再提供类似于友元这样的特性了。大家都知道在C++中对象的私有成员,外部是无......
  • C++逆向分析——运算符重载
    运算符重载现在有一个类,其中有一个函数用于比较2个类的成员大小:#include<stdio.h> classNumber{private:intx;inty;public:Number(intx,inty){this->x=x;this->y=y;}intMax(Number&n){returnthis->x>n.x&&this->y>n.......
  • C++逆向分析——模版
    模版假设有一个冒泡排序的函数:voidSort(int*arr,intnLength){inti,k;for(i=0;i<nLength;i++){for(k=0;k<nLength-1-i;k++){if(arr[k]>arr[k+1]){inttemp=arr[k];arr[k]=arr[k+1];arr[k+1]=temp;}}}}但是这个冒泡......
  • C++逆向分析——构造函数和析构函数
    构造函数与析构函数构造函数structStudent{inta;intb; Student(){printf("Look.");} voidInit(inta,intb){this->a=a;this->b=b;} };如上代码中,我们发现了存在一个函数,这个函数没有返回类型并且与结构体名称一样,那这段函数在什么......
  • Code-C++ Invoke Python
    Code-C++InvokePythonhttps://www.cnblogs.com/yongchao/p/17299892.html使用C或C++扩展Python扩展和嵌入Python解释器Python3.10.11Python/CAPI参考手册Python3.11.3Python/CAPI参考手册https://www.cnblogs.com/lidabo/p/17043302.htmlhttps://bl......