内存对齐用法小结

简介: 今天偶然在贴吧里面看到有人问 struct A {   char a;   double b; };   使用C++的 sizeof(A); 结果为:16。

今天偶然在贴吧里面看到有人问

struct A

{

  char a;

  double b;

};

 

使用C++的

sizeof(A);

结果为:16。

 

已经周折才把这个搞明白了。

 

下面我来说一下,看你能看的懂吗:

 

从大的方面来讲,今天我们说的这个内存边界对准,其实是为了提高内存访问速率,同时也提高了程序的可移植性。

我主要说明提高内存访问速率。

 

首先介绍一个事实,如果我们语言里面的内置数据类型所声明的对象没有按照其相应的内存边界对准,那么存储器很有可能要多次访存。

为什么?

假设有这么一种处理器,它只能访问8的倍数的内存地址(实际上现在的cpu基本都是这样,但不是8倍),而且每次写入或者读取都是8个字节。这么一来对我们的编程就造成影响了。假如有个double b;b的地址假设是0006,那么b所占据的地址段就0006-000D(16进制)。那么好了,这个cpu是不能在一次访问存储器就能取得这个数据的。它需要先访问0地址,取出0006-0007的数据,然后再访问0008-000D的数据。

 

但是要是我们本来就把double b;放在一个8倍的地址上,那么cpu就可以一次访问存储器就可以取得这个数据了。

所以结论是:内存地址对齐是很有必要的,速度提高了一倍哦。

 

移植性是因为有的cpu架构对某些类型只能够从特定形式的内存地址存取,否则就会出错。所以对于这类平台必须使用内存边界对准,而其他平台没有这类问题,所以按地址边界做的比较好,对一般机器可以,对别的机器也可以,所以提高了移植性。

 

接着我们在讲究竟是怎么对齐。我讲讲结构体的内存对齐问题:

假如:

struct A

{

    char a;

    short b;

    int b;

};

 

那么sizeof(A),究竟是多少呢?

答案是:8

 

按照一般计算式:1+2+4=7

 

但是内存边界对准有3条规则:

首先一般编译器自身就有一个默认的内存对齐模数(内存对齐模数就是以几倍地址对齐,模数就是几)是4或者8。VC应该是4

规则1:结构体内部数据的对齐,拿结构体的数据成员的大小(sizeof值)和默认对齐模数,谁小,就以谁为模数。

规则2:结构体本身的内存对齐,拿结构体内的数据成员的大小中最大的和默认对齐模数比较,谁小,就以谁为模数。

规则3:其实也是个定理,如果默认对齐模数比结构体中最大数据成员大是,那么默认对齐模数没用。

 

模数到底有什么用呢?看下面的实例:

 

用这个规则我们来看一下上面的struct A。假设默认对齐模式为4

struct A

{

    char a;//大小为1,因为1<4,所以以1为对齐模数,意思就是这个数据成员的地址只要是1的倍数就可以了,那么也就是任意地址。

               //那我们假设这个结构体的其实地址是0,那么a的地址是0。     即a=[0]

    short b;//大小为2,因为2<4,所以以2为对齐模数,意思就是这个数据成员的地址只要是2的倍数就可以了,那么也就是必须放在

                 //0,2,4,8为尾数的地址上,前面的a地址是0,大小是1,那么这样,后面的变量按理是在1地址上,但是刚才也说了必须要在

                //0,2,4,8为尾数的地址上,所以b必须放在2地址上。             即b=[2,3]

     int c;//大小为4,因为4=4,那么以4为对齐模数,所以c的地址必须尾数是4的倍数,正好可以放在地址4上。

               //                                                                                   即c=[4,5,6,7]

};

 

所以结构体A的数据成员的总大小是8。

但并没有完,虽然结果是8。

实际上编译器还要执行规则二。

那就是:用数据结构的最大数据成员大小MAX(char,short,int)=4,和默认的对齐模数4比较,选择较小的那个,所以以4为模数。

结构体的大小 =为了使 ( 数据结构成员的总大小 / 对齐模数 )圆整,而调节数据结构成员的大小后的值。

 

圆整的意思是: 3 / 4 结果是0.75,对结果圆整是1,调整3变成4,那么结果就是整数了。

 

所以结构体的大小=(8/4)圆整调后后的结果=8

 

但并未总是8.

 

如结构体A

{

    double a;

    char b;

}

 

数据成员大小是9

 

但结构体的大小=12。就是因为圆整。

 

但为什么要调整结构体的大小呢?(我想)

原因应该是就是满足对齐要求,时候后面变量分配的地址能满足对齐模数的要求。

如果结构体大小为9,那么后面结构体的变量开始地址从9开始,就很麻烦了。

相关文章
|
5月前
10分钟让你学会内存函数:memcpy,memmove,memset,memcmp的用法及模拟实现。
10分钟让你学会内存函数:memcpy,memmove,memset,memcmp的用法及模拟实现。
75 2
|
3月前
|
存储 程序员 编译器
c++学习笔记08 内存分区、new和delete的用法
C++内存管理的学习笔记08,介绍了内存分区的概念,包括代码区、全局区、堆区和栈区,以及如何在堆区使用`new`和`delete`进行内存分配和释放。
47 0
|
Linux C语言
Linux内存管理:详解malloc()和free()函数的用法与原理
在C语言中,动态内存分配是一项关键任务,而`malloc()`和`free()`函数则是实现动态内存分配的重要工具。本文将深入介绍这两个函数的使用方法、内部原理以及注意事项,帮助读者更好地理解如何进行内存管理。
1146 0
内存中的栈(stack)、堆(heap)和静态区(static area)的用法
通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序中的字面量(literal)如直接书写的100、"hello"和常量都是放在静态区中。
1282 0
|
C语言 C++
【C 语言】const 关键字用法 ( 常量指针 - const 在 * 左边 - 修饰数据类型 - 内存不变 | 指针常量 - const 在 * 右边 - 修饰变量 - 指针不变 )
【C 语言】const 关键字用法 ( 常量指针 - const 在 * 左边 - 修饰数据类型 - 内存不变 | 指针常量 - const 在 * 右边 - 修饰变量 - 指针不变 )
139 0
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
369 0
|
22天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
45 1