精华内容
下载资源
问答
  • 内存对齐内存对齐规则解释、内存对齐原理

    千次阅读 多人点赞 2020-03-28 23:44:31
    一、内存对齐的原因 我们都知道计算机是以字节(Byte)为单位划分的,理论上来说CPU是可以访问任一编号的字节数据的,我们又知道CPU的寻址其实是通过地址总线来访问内存的,CPU又分为32位64位,在32位的CPU一次...

    一、内存对齐的原因

    我们都知道计算机是以字节(Byte)为单位划分的,理论上来说CPU是可以访问任一编号的字节数据的,我们又知道CPU的寻址其实是通过地址总线来访问内存的,CPU又分为32位和64位,在32位的CPU一次可以处理4个字节(Byte)的数据,那么CPU实际寻址的步长就是4个字节,也就是只对编号是4的倍数的内存地址进行寻址。同理64位的CPU的寻址步长是8字节,只对编号是8的倍数的内存地址进行寻址,如下图所示是64位CPU的寻址示意图:
    在这里插入图片描述

    这样做可以实现最快速的方式寻址且不会遗漏一个字节,也不会重复寻址。

    那么对于程序而言,一个变量的数据存储范围是在一个寻址步长范围内的话,这样一次寻址就可以读取到变量的值,如果是超出了步长范围内的数据存储,就需要读取两次寻址再进行数据的拼接,效率明显降低了。例如一个double类型的数据在内存中占据8个字节,如果地址是8,那么好办,一次寻址就可以了,如果是20呢,那就需要进行两次寻址了。这样就产生了数据对齐的规则,也就是将数据尽量的存储在一个步长内,避免跨步长的存储,这就是内存对齐。在32位编译环境下默认4字节对齐,在64位编译环境下默认8字节对齐。

    查看自己电脑是多少位操作系统
    终端下输入:uname -a 回车
    x86_64 表示系统为64位
    i686 表示系统32位的

    二、对齐规则
    1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的偏移为 #pragma pack 指定的数值和这个数据成员自身长度中较小那个的整数倍。
    2:数据成员为结构体:如果结构体的数据成员还为结构体,则该数据成员的“自身长度”为其内部最大元素的大小。(struct a 里存有 struct b,b 里有char,int,double等元素,那 b “自身长度”为 8)
    3:结构体的整体对齐规则:在数据成员按照 #1 完成各自对齐之后,结构体本身也要进行对齐。对齐会将结构体的大小增加为 #pragma pack 指定的数值和结构体最大数据成员长度中较小那个的整数倍。

    (看不懂没关系,因为我也没看懂,也是打印之后才明白)

    三、实例

    typedef struct test1 {
        char a;//1
        int b;//4字节
        double c;//8
        char d[11];//11
    }Test1;//数据成员
    
    int main(int argc, const char * argv[]) {
    
        Test1 t1;
        //xcode默认对齐系数8,即#pragma pack(8)
        //char a,     1<8按1对齐,offset=0            [0]
        //int b,      4<8按4对齐,char a占到[0],int b应该从地址1开始排, 地址1不是对齐数4的倍数,所以int b的首地址是从4的最小倍数开始,所以offset=4,       [4...7];
        //double c,   8=8按8对齐,int b已经占到[4...7],double c从地址8开始排,地址8是对齐数8的倍数,所以double c的首地址是从8的最小倍数开始,offse=8             [8...15]
        //char d[11], 1<8按1对齐,double c已经占到[8...15], char d[11]从地址16开始,地址16是对齐数1的倍数,所以char d的,offset=16,          存储位置[16...26]
        //最后为27位,又因为27不是内部最大成员中double 8位字节的倍数,所以补齐为32)
        printf(" %lu\n %p\n %p\n %p\n %p\n", sizeof(t1), &t1.a, &t1.b, &t1.c, &t1.d);
        
        return 0;
    }
    

    结果:

     32
     0x7ffeefbff568
     0x7ffeefbff56c
     0x7ffeefbff570
     0x7ffeefbff578
    

    以首地址0x7ffeefbff568为offset=0,
    0x68=104,
    0x6c=108,
    0x70=112,
    0x78=120,
    0x7ffeefbff56c相对于0x7ffeefbff568便宜了4,
    0x7ffeefbff570相对于0x7ffeefbff568便宜了8,
    0x7ffeefbff578相对于0x7ffeefbff568偏移了16,符合规律
    上面int b正好偏移4,double c正好偏移8,比较凑巧,如果char a[5]呢,使地址正好措开,此时int b应该是[8…13],double c应该是[16…23], char d[11]应该是[24…34],34不是最大double c的整数倍,补齐到最小倍数40,所以结构体的大小应该是40.

     40
     0x7ffeefbff560
     0x7ffeefbff568
     0x7ffeefbff570
     0x7ffeefbff578
    Program ended with exit code: 0
    

    在结构体struct Test1中添加一个结构体Test2,看下结果:

    typedef struct test2 {
        char a[13];//1      [0...13]
        double b;//8        [16...23]
        int c[11];//4       [24...67]
        float d;//4         [68...71]
    }Test2;
    
    typedef struct test1 {
        char a[5];//1       [0...4]
        int b;//4           [8...11]
        double c;//8        [16...23]
        char d[11];//11     [24...34]
        Test2 t2;//         [40...111] Test2的'自身长度'为double b=8,所以从8的最小倍数开始,即40
    }Test1;//数据成员
    //此时:
    //  char a[13];//1      [40...52]
    //  double b;//8        [56...63]
    //  int c[11];//4       [64...107]
    //  float d;//4         [107...111]
    
    int main(int argc, const char * argv[]) {
        
        Test1 t1;
        Test2 t2;
        printf(" %lu\n", sizeof(t2));
        printf(" %lu\n", sizeof(t1));
        return 0;
    }
    

    结果:

     72
     112
    Program ended with exit code: 0
    

    把Test2中的double b注销了,看下结果:

    typedef struct test2 {
        char a[13];//1      [0...13]
        //double b;//8        [16...23]
        int c[11];//4       [16...59]
        float d;//4         [60...63]
    }Test2;
    
    typedef struct test1 {
        char a[5];//1       [0...4]
        int b;//4           [8...11]
        double c;//8        [16...23]
        char d[11];//11     [24...34]
        Test2 t2;//64       [36...99] 此时Test2的'自身长度'为int c=4,所以从4的最小倍数开始,即36开始,又因为100不是double c的倍数,补齐到最小倍数104
    }Test1;//数据成员
    
    int main(int argc, const char * argv[]) {
        
        Test1 t1;
        Test2 t2;
        printf(" %lu\n", sizeof(t2));
        printf(" %lu\n", sizeof(t1));
        return 0;
    }
    
     64
     104
    Program ended with exit code: 0
    

    四、更改默认对齐系数

    #pragma pack(2)//1、2、4、8、16,以2为例
    
    typedef struct test2 {
        char a[13];//1      [0...13]
        //double b;//8        [16...23]
        int c[11];//4    2<4   [14...57]从2的最小倍数开始,即14
        float d;//4         [58...61]
    }Test2;
    int main(int argc, const char * argv[]) {
        
        Test2 t2;
        printf(" %lu\n", sizeof(t2));
        return 0;
    }
    

    结果:

     62
    Program ended with exit code: 0
    

    参考:

    https://www.jianshu.com/p/f01fe1ef892d

    展开全文
  • 关于内存对齐的很详细的解析.内容有什么是内存对齐,为什么需要内存对齐,编译器对内存对齐的处理,栈内存对齐等,并附有验证代码.值得一看.
  • 内存对齐模式定义

    内存对齐模式定义:
    当前变量的首地址,与当前变量的首地址有关,即:若当前类型的类型是type,那么,当前变量的首地址必须是sizeof(type)的整数倍
    例如:定义这样一个结构体:
    typedef struct NEW_TYPE{
    int one;
    char two;
    short three;
    double four;
    };
    假设这个结构体第一个变量从编号为0的空间开始存储,那么,该结构体所占空间如下图所示:
    在这里插入图片描述

    那么,为什么计算机存储数据要使用内存对齐模式呢?
    首先,为了与之后所讲内存管理模式形成对比,我先提及一下非页式管理:
    非页式管理的原理是连续存储空间的分配与回收。
    而连续存储空间的分配与回收,会造成大量的内存碎片,这种碎片被称为“外部碎片”;而在碎片过多的情况下,会出现:所有可用空间之和满足要求,但是,任何一个空闲碎片都不满足要求而导致内存分配失败的现象。
    为了解决这个现象,可以进行“压缩”,即:将所有已分配空间移动在一起,将所有可用空间“合并”成一个大的可用空间,但,这种操作非常消耗系统资源,同时,要涉及复杂的“重定位”技术。

    总的来看,上述问题的根本原因是:内存的连续存储分配。<

    展开全文
  • 定义 理论上说,任何变量的访问其实都可以从任何的内存地址开始,即变量的存储内存地址是无关性的。但实际情况却由于各种原因(比如可...这就是内存对齐 以下代码可以大概解释内存对齐问题: struct st{ char...

    定义

    理论上说,任何变量的访问其实都可以从任何的内存地址开始,即变量的存储和内存地址是无关性的。但实际情况却由于各种原因(比如可移植性、性能等等),某些特定的变量必须在特定的内存地址上被存储和访问。这种需求就要求各种数据类型必须按照一定的既有规则在内存空间上进行排列。而不是一个一个的按照位置紧挨着排列。这就是内存对齐
    以下代码可以大概解释内存对齐问题:

    struct st{
           char a;
           int b;
           char c;
    };
    

    对该结构体的内存分析如下图所示:
    在这里插入图片描述
    系统禁止编译器在一个结构的起始位置跳过几个字节使边界满足对齐要求,所有结构起始存储位置需是结构中边界要求最严格的数据类型所要求的位置,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是内存对齐,而这个k则被称为该数据类型的对齐模数。因此,a存储与一段能被4整除的地址,b、c亦然

    首先得掌握结构体的对齐规则:

    1. 第一个成员在与结构体变量偏移量为0的地址处
    2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认的值为8,Linux中的默认值为4)
    3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
    4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

    意义

    结构体内存对齐实际是一种拿空间换时间的做法:

    1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
    2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问

    运用

    如以下两段代码所示:

    struct S1
    {
       char c1;
       int i;
       char c2;
    };
    printf("%d\n", sizeof(struct S1));
    
    struct S2
    {
       char c1;
       char c2;
       int i;
    };
    printf("%d\n", sizeof(struct S2));
    

    struct1所占内存为12个字节,而struct2所占内存为8个字节,struct2中b、c可以紧挨着存储,显然struct2节省了空间
    所以在设置结构体时,应该注意先对结构体成员重新排序,使边界要求最严格的成员先出现,依次排列,直至边界要求最弱的成员出现,防止内存对齐造成的空间浪费

    展开全文
  • C++ 中内存对齐原理及作用

    千次阅读 多人点赞 2018-01-07 16:54:46
    struct/class/union内存对齐原则有四个:1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小...

    struct/class/union内存对齐原则有四个:

    1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节, 则要从4的整数倍地址开始存储),基本类型不包括struct/class/uinon。

    2).结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部”最宽基本类型成员“的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。

    3).收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的”最宽基本类型成员”的整数倍.不足的要补齐.(基本类型不包括struct/class/uinon)。

    4).sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址。

    实例解释:下面以class为代表

    No.1

    class Data
    {
        char c;
        int a;
    };
    
    cout << sizeof(Data) << endl;

    No.2

    class Data
    {
        char c;
        double a;
    };
    
    cout << sizeof(Data) << endl;

    显然程序No.1 输出的结果为 8 No.2 输出的结果为 16 .

    No.1最大的数据成员是4bytes,1+4=5,补齐为4的倍数,也就是8。而No.2为8bytes,1+8=9,补齐为8的倍数,也就是16。

    No.3

    class Data
    {
        char c;
        int a;
        char d;
    };
    
    cout << sizeof(Data) << endl;

    No.4

    class Data
    {
        char c;
        char d;
        int a;
    };
    
    cout << sizeof(Data) << endl;

    No.3运行结果为 12 No.4运行结果为 8

    class中的数据成员放入内存的时候,内存拿出一个内存块来,数据成员们排队一个一个往里放,遇到太大的,不是把自己劈成两半,能放多少放多少,而是等下一个内存块过来。这样的话,就可以理解为什么No.3,No.4两端的代码输出结果不一样了,因为左边是1+(3)+4+1+(3)=12,而右边是1+1+(2)+4=8。括号中为补齐的bytes。

    No.5

    class BigData
    {
        char array[33];
    };
    
    class Data
    {
        BigData bd;
        int integer;
        double d;
    };
    
    cout << sizeof(BigData) << "   " << sizeof(Data) << endl;

    No.6

    class BigData
    {
        char array[33];
    };
    
    class Data
    {
        BigData bd;
        double d;
    };
    
    cout << sizeof(BigData) << "   " << sizeof(Data) << endl;

    No.5和No.6运行结果均为: 48

    在默认条件下,内存对齐是以class中最大的那个基本类型为基准的,如果class中有自定义类型,则递归的取其中最大的基本类型来参与比较。在No.5和No.6中内存块一个接一个的过来接走数据成员,一直到第5块的时候,BigData里只剩1个char了,将它放入内存块中,内存块还剩7个bytes,接下来是个int(4bytes),能够放下,所以也进入第5个内存块,这时候内存块还剩3bytes,而接下来是个double(8bytes),放不下,所以要等下一个内存快到来。因此,No.5的Data的size=33+4+(3)+8=48,同理No.6应该是33+(7)+8=48。

    顺便提一下Union: 共用体表示几个变量共用一个内存位置,在不同的时间保存不同的数据类型和不同长度的变量。在union中,所有的共用体成员共用一个空间,并且同一时间只能储存其中一个成员变量的值。

    No.7

    class A
    {
        int i;
        char c1;
    }
    
    class B:public A
    {
        char c2;
    }
    
    class C:public B
    {
        char c3;
    }

    sizeof(C)结果是多少呢,gcc和vs给出了不同的结果,分别是8、16

    gcc中:C相当于把所有成员i、c1、c2、c3当作是在一个class内部,(先继承后对齐)

    vs中:对于A,对齐后其大小是8;对于B,c2加上对齐后的A的大小是9,对齐后就是12;对于C,c3加上对齐后的B大小是13,再对齐就是16 (先对齐后继承)

    关于c++对象继承后的内存布局,更详细的分析可以《深度探索参考c++对象模型》第三章

    内存对齐的主要作用是:

    1、 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

    2、 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。具体原因稍后解释。

    这里写图片描述

    这是普通程序员心目中的内存印象,由一个个的字节组成,而CPU并不是这么看待的。

    这里写图片描述

    CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。块大小成为memory access granularity(粒度) 本人把它翻译为“内存读取粒度” 。

    假设CPU要读取一个int型4字节大小的数据到寄存器中,分两种情况讨论:

    1、数据从0字节开始

    2、数据从1字节开始

    再次假设内存读取粒度为4。

    这里写图片描述

    当该数据是从0字节开始时,很CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。

    当该数据是从1字节开始时,问题变的有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据。

    这里写图片描述

    此时CPU先访问一次内存,读取0—3字节的数据进寄存器,并再次读取4—5字节的数据进寄存器,接着把0字节和6,7,8字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU性能。

    这还属于乐观情况了,上文提到内存对齐的作用之一为平台的移植原因,因为以上操作只有有部分CPU肯干,其他一部分CPU遇到未对齐边界就直接罢工了。

    展开全文
  • 内存对齐目的

    2020-03-13 09:26:46
    1. 内存对齐目的 内存对齐跟内存物理结构有关。64位计算机的内存一般有64个电容chip,每个chip对应64位的每一位。一次内存IO可以从每个chip上同时读出一位,组成64位字长。 CPU希望每次读内存都能直接读出一...
  • 内存对齐原理 由于CPU在读取计算数据时,需要根据CPU操作系统的位数来一次性读取一定大小的数据。目前大部分CPU都支持64位,操作系统大部分都使用32位或64位,我们以64位操作系统为例进行说明。64位操作系统一...
  • golang内存对齐原理

    2020-11-30 20:16:27
    内存对齐的现象: 之前没接触过c艹的同学可以先看下面的例子: type A struct { a bool b string c bool } type B struct { a bool c bool b string } func main() { fmt.Printf("Size A:%d\n", unsafe....
  • 内存对齐原理 内存对齐的原因 1.平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 2.性能原因: 内存对齐...
  • 在探讨内存对齐原理之前,首先介绍下iOS中获取内存大小的三种方式 获取内存大小的三种方式 获取内存大小的三种方式分别是: sizeof class_getInstanceSize malloc_size sizeof 1、sizeof是一个操作符,不是函数 2...
  • GO语言-内存对齐

    2021-01-28 17:10:16
    # 内存对齐
  • 在iOS中所有的类都是... 结构体内存对齐 先让我们看一个小案例: //1、定义两个结构体 struct AAA{ char a; //1字节 double b; //8字节 int c; //4字节 short d; //2字节 }AAA; struct BBB{ double b; //8字节
  • 数据内存对齐

    2018-11-24 11:33:40
    比如他听说过内存对齐和其大概的作用,但是却不知道cache对齐以及对齐到底有什么作用,更不了解怎么能更好的对结构进行内存布局以提高性能,在本文,你会得到解答。 以下讨论的概念性的东西应该都是适用于所有系统的...
  • 原文: http://www.cnblogs.com/jijiji/p/4854581.html
  • 内存对齐对性能的影响 CPU读取内存的方式 CPU读取内存的方式其实非常复杂,为了更进一步的了解相关的原理,需要对计算机组成原理进行深度学习。 Memory access granularity(内存读取粒度) However, your computer’s...
  • 在C语言程序开发中,有时有经验的程序员会提起“内存对齐”一词,事实上,这也是C语言中结构体的 size 不等于它所有成员 size 之的原因(C语言中的结构体的size,并不等于它所有成员size之,为什么?),那么,...
  • 回顾 上篇我们简单的阐述了一下面向对象与面向过程编程的基本思想以及它们之间的区别,对我们类与对象做了一个简单的了解,那么这次...我们知道对象是一个类的实例化,类是一个抽象的概念,而对象占有实际的物理内存...
  • 首先,内存对齐是要提高系统的内存利用率提高CPU处理速率。 我们先来看一个例子: typedef struct structA { char c1; char c2; short s1; double d1; } structA; typedef struct structB { char c1; short...
  • 认识字节对齐之前,假定int(4Byte),char(1Byte),short(2Byte)认识字节对齐先看段代码:struct Data1{char a;int b;short c;};struct Data2 {int a;char b;short c;};int main(){cout << sizeof(Data1) <&...
  • 32位cpu为例,cpu在访问内存的时候是以一个字进行传输的(计算机中,一个机器字长就是cpu位数,32位cpu,四个字节为一个字),那么也就是说cpu读取数据每次都是四个字节四个字节的读取,计算机最初是从地址0开始寻址...
  • C语言中内存对齐详解

    2016-04-26 16:08:39
    在解释内存对齐的作用前,先来看下内存对齐的规则: 1、 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身长度) 的倍数。 ...
  • 从内存的角度解释内存对齐原理

    千次阅读 2012-03-16 22:08:25
    从内存的角度解释内存对齐原理 队列原理 Alignment fundamentals Lazy processors 二 速度 Speed (内存对齐的基本原理) 代码解释 中文代码及其内存解释 三 不懂内存对齐将造成的可能影响如下 ...
  • 1.结构体内存对齐的原因? 什么是对齐,以及为什么要对齐: 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的...
  • cpu在访问内存的时候是以一个字(字节)进行传输的(计算机中,一个机器字长就是cpu位数,32位cpu,四个字节为一... 如果不指定对齐方式,默认单字节对齐,即最自然的变量申请存放方式,这样不会造成内存空隙浪费,但.
  • 内存对齐的初步讲解 内存对齐可以用一句话来概括: “数据项只能存储在地址是数据项大小的整数倍的内存位置上(分别为偶地址、被4整除的地址、被8整除的地址 )” 例如int类型占用4个字节,地址只能在0,4,8等...
  • 内存对齐-笔记

    2020-03-09 00:58:49
    1. 内存简单原理 内存是由chip构成。每个chip内部,是由8个bank组成的。其构造如下图 每个bank内部,就是电容的行列矩阵结构了。(注意,二维矩阵中的一个元素一般存储着8个bit,也就是说包含了...
  • C++内存对齐

    2020-07-28 21:53:49
    一、内存对齐的定义 字节对齐主要是为了提高内存的访问效率,比如intel 32位cpu,每个总线周期都是从偶地址开始读取32位(4字节)的内存数据,如果数据存放地址不是从偶数开始,则可能出现需要两个总线周期才能读取...
  • 内存对齐算法

    2020-10-24 16:59:38
    假定目前该变量占用n个字节,对齐目标是align,求返回的占用实际内存大小。 (1)最容易想到的算法: unsigned int calc_align(unsigned int n,unsigned align) { if ( n / align * align == n) return n; return ...
  • 内存对齐原理,作用,例子以及一些规划(中英文说明,适用sizeof结构体) 目录 题记  一 内存读取粒度 Memory access granularity   从内存的角度解释内存对齐原理 队列原理 Alignment...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,300
精华内容 10,120
关键字:

内存对齐的目的和原理