内存对齐
四字节
|--------int--------| 4字节
|char|----|--short-| 4字节
总共占8字节
3.test3
?
typedef struct node2
{
char a;
int b;
short c;
}S2;
则siezof(S3)=12.最长数据类型为int,占4个字节。因此以4字节对齐,其在内存空间存放方式如下:
short b;
static int c;
}S3;
则sizeof(S3)=8.这里结构体中包含静态数据成员,而静态数据成员的存放位置与结构体实例的存储地址无关(注意只有在C++中结构体中才能含有静态数据成员,而C中结构体中是不允许含有静态数据成员的)。其在内存中存储方式如下:
同理,分析上面例子C:
#pragma pack (2) /*指定按2字节对齐*/
struct C {
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
第 一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1= 0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续 字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放
例子分析:
分析例子B;
struct B {
char b;
ARM处理器下的内存对齐问题
ARM处理器下的内存对齐问题介绍内存访问可以分为aligned和未对齐unaligned.对齐内存访问发生在数据分配在natural size boundary,如果这个数据的大小是4 bytes,而且它分配的地址可以被4整除,它就是分配在natural size boundary的,它就是内存对齐的.未对齐内存访问就是其他的所有情况(内存地址不能被4整除);ARM处理器被设计成可以高效的访问对齐数据,在ARM处理器上尝试访问未对齐内存数据将得到两种结果:错误数据或显著的执行差异(很快会讨论这些不同的表现).这不同于其他的CISC类型的处理器,它们可以正常的访问未对齐数据.这篇文档将会描述一些对应用程序来说通用的方式处理未对齐内存访问和提供一些推荐的解决方案以解决这些问题.症状上述问题针对所有ARM架构的.然而,根据MMU是否使能和操作系统的支持,应用程序在不同的平台上有不同的表现.在默认情况下,未对齐内存访问不会被捕捉,而是返回一个错误数据.在使能了MMU的平台上,OS将可以捕捉未对齐内存访问而且在运行时调整正确.返回的结果将是正确的数据,但是将花费 10-20个cpu周期.通常原因类型分配Code:void my_func(char *a){int *b = (int *)a;DBGPRINTF("%d", *b);}这个简单的例子可以生成未对齐内存访问,因为我们不能保证参数char* a是在4字节边界上的.这样的类型定义在任何时候都应该避免.使用数据buffer大多数常见的未对齐内存访问发生在错误的处理数据buffer,这些数据buffer可能包含任何从usb端口,网路,或文件中读取的数据.通常会设置这些数据为packed,意味着没有padding嵌入以确保buffer中的数据是natural size boundary的.在这个例子中,我们将讨论装载从一个文件一个windows BMP格式数据,然后解析其文件头的情况.一个windows BMP文件包含一个以下数据项的文件头,文件头由两个结构体组成:Code:typedef PACKED struct{unsigned short int type;unsigned int size;unsigned short int reserved1, reserved2;unsigned int offset;} HEADER;typedef PACKED struct{unsigned int size;int width,height;unsigned short int planes;unsigned short int bits;unsigned int compression;unsigned int imagesize;int xresolution,yresolution;unsigned int ncolours;unsigned int importantcolours;} INFOHEADER;注意HEADER和INFOHEADER结构体的大小分别是14和40字节.假设我们想在程序运行是检测图片的宽带和高度,得到这些数据的代码如下:Code:#define INFOHEADER_OFFSET (sizeof(HEADER))#define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width))#define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height))int imageWidth, imageHeight;void * fileBuf;pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ);if (pMe->mFile){IFILE_GetInfo(pMe->mFile, &fileInfo);fileBuf = MALLOC(fileInfo.dwSize);if (fileBuf){result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize);if (result == fileInfo.dwSize){imageWidth = *((uint32*)(((byte*)fileBuf) + WIDTH_OFFSET));imageHeight = *((uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET));}}}注意宽度和高度的偏移.因为它们位于一个half-word boundary,以上面的代码访问它们的值将是未对齐内存访问.一些推荐的方式解决这个问题如下:推荐方案使用memcpy我们第一个选择是简单的使用memcpy将数据从buffer处理到我们的局部变量中:Code:if (result == fileInfo.dwSize){MEMCPY(&imageWidth,(((byte*)fileBuf)+WIDTH_OFFSET),sizeof(uint32));MEMCPY(&imageHeight,(((byte*)fileBuf)+HEIGHT_OFFSET),sizeof(uint32));}结果是内存被紧密的拷贝,避免了对齐问题.使用PACKED编译指令或者,我们可以使用PACKED编译指令以允许使用指针直接的访问我们想要的数据.也就是强制编译器处理对齐问题.在BREW环境下,PACKED定义如下:Code:#ifdef __ARMCC_VERSION#define PACKED __packed#else#define PACKED#endif通过标明一个指针是PACKED的,ARM编译器将始终生成合适的指令可以正确的访问内存.不管对齐,上边的例子的一个修改的版本,使用PACKED的指针,如下:Code:#define INFOHEADER_OFFSET (sizeof(HEADER))#define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width))#define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height))PACKED uint32 * pImageWidth;PACKED uint32 * pImageHeight;uint32 imageWidth, imageHeight;void * fileBuf;pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ);if (pMe->mFile){IFILE_GetInfo(pMe->mFile, &fileInfo);fileBuf = MALLOC(fileInfo.dwSize);if (fileBuf){result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize);if (result == fileInfo.dwSize){pImageWidth = (uint32*)(((byte*)fileBuf) + WIDTH_OFFSET);pImageHeight = (uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET);imageWidth = *pImageWidth;imageHeight = *pImageHeight;}}}定义Well-Aligned数据结构虽然我们一般不能控制定制的数据格式,比如上面例子中的BMP文件头,但是,当我们定义自己的数据结构我们可以将数据设计成Well-Aligned方式.以下例子演示这种方式:Code:#ifdef __ARMCC_VERSIONtypedef PACKED struct{short a; // offsetof(a) = 0int b; // offsetof(b) = 2 ?misalignment problem!short c; // offsetof(c) = 6} BAD_STRUCT;typedef struct{int b; // offsetof(b) = 0 ?no problem!short a; // offsetof(a) = 4short c; // offsetof(c) = 6} GOOD_STRUCT;简单的重新定义结构提成员的顺序,我们可以解决一些对齐问题.同时注意如果BAD_STRUCT没有定义为PACKED,编译器一般将会插入padding以使每个成员是Well-Aligned的.然而,这通常是不可取的,因为它浪费内存,而且几乎总是可以通过按顺序声明减少大小而避免。
计算机内存对齐原理
计算机内存对齐原理计算机内存对齐原理是指在计算机系统中,为了提高效率和正确性,数据在内存中的存储会被按照一定的规则进行排列。
这一原理涉及到了计算机硬件和软件的多个层面。
1. **数据对齐(Data Alignment)**:数据对齐是指数据在内存中的存放位置是按照一定的边界进行的。
比如,在许多计算机体系结构中,数据通常按照4字节或8字节的边界对齐。
这意味着数据的地址通常是这些边界数的倍数。
这样做的目的是为了提高数据访问的效率,因为非对齐的访问通常需要额外的硬件指令来处理,这会增加访问时间。
2. **内存访问效率**:当数据在内存中正确对齐时,处理器可以更高效地访问这些数据。
例如,如果处理器一次可以读取4字节,那么它最好是从一个4字节的边界开始访问,这样一次就能读取到整个数据块,而不需要分多次读取。
3. **指令集架构(ISA)的规定**:不同的处理器架构有不同的内存对齐要求。
例如,x86架构就有严格的数据对齐要求,而ARM架构则相对宽松。
软件开发者在编写程序时需要遵循目标处理器的ISA规定。
4. **编译器和操作系统的作用**:现代编译器和操作系统会自动处理内存对齐,以确保数据按照最佳方式存储。
它们会根据目标处理器的特点来优化代码,包括数据的内存布局。
5. **性能考量**:虽然内存对齐可以提高性能,但它也可能增加内存的使用量。
因为为了对齐数据,可能需要在数据前后填充一些无用的字节,这被称为填充(Padding)。
因此,在设计数据结构时,开发者需要在性能和内存使用之间做出权衡。
内存对齐是计算机体系结构中的一个重要概念,它影响着程序的性能和效率。
程序员在编写代码时应当了解并遵循内存对齐的原则,以优化程序的运行速度。
同时,内存对齐也是操作系统和编译器优化代码的一个重要方面。
如何通过内存对齐提高程序性能
内存对齐是一种优化程序性能的重要手段。
它可以使得数据在内存中的存储更加紧凑和高效,减少内存访问的次数和开销,从而提高程序的运行速度。
本文将从什么是内存对齐、为何需要内存对齐以及如何通过内存对齐提高程序性能等方面展开论述。
一、什么是内存对齐内存对齐是指内存中的数据在存储时按照一定的规则对齐,如按字节对齐、按字对齐等。
在现代计算机中,数据访问通常以字节为单位进行,而内存对齐能够使得数据的存储地址整除数据类型的大小。
例如,一个int类型的变量通常占用4个字节,内存对齐能够保证它存储的地址是4的倍数,而不是随机的地址。
二、为何需要内存对齐内存对齐的主要目的是提高数据存取的效率。
当数据按照字节对齐存储时,CPU在访问内存时无需额外的计算和操作,可以直接通过内存地址来获取数据,加快访问速度。
相反,如果数据没有对齐存储,CPU就需要进行额外的位移和掩码操作,这会造成额外的时间和开销。
三、内存对齐的原则1. 基本类型的变量,如int、float,通常按照其本身的大小进行对齐。
例如,一个int类型的变量通常按照4字节对齐存储。
2. 结构体的对齐规则通常是按照最大成员的大小进行对齐。
例如,一个结构体中最大的成员是8字节的double类型变量,那么结构体就按照8字节对齐存储。
3. 编译器一般会对结构体进行填充,以满足对齐的要求。
这样可以使得结构体的大小是对齐大小的整数倍,从而提高内存访问的效率。
4. 对于特殊情况和对齐要求更高的场景,可以使用编译器提供的对齐指令来自定义对齐规则。
四、如何通过内存对齐提高程序性能1. 减少内存访问次数:由于内存对齐可以使得数据在内存中的存储更加紧凑,减少了数据的分散存储,从而可以减少内存访问的次数。
对于大型数据结构或数组,内存对齐能够显著提升对内存的访问效率,加快程序的运行速度。
2. 提高缓存命中率:CPU的高速缓存是一个重要的性能瓶颈,内存对齐可以提高缓存命中率。
当数据按照对齐规则存储时,缓存可以更好地预取和预存储数据,减少了对主存的访问次数,从而提高程序的运行效率。
内存对齐原则
内存对齐原则内存对齐原则是计算机科学中一个非常重要的概念,它在程序运行过程中起着至关重要的作用。
内存对齐是指数据在内存中存储时按照一定的规则对齐到特定的地址上,以提高程序的运行效率。
在计算机中,数据的存储是以字节为单位的,每个数据类型在内存中占用的字节数是不同的。
而内存对齐原则就是要求数据类型的起始地址必须是其大小的整数倍。
比如,一个int类型通常占用4个字节,那么它的起始地址必须是4的倍数。
为什么需要内存对齐呢?这是因为计算机在访问内存时,通常是以字为单位进行读取的。
如果数据没有按照规则对齐存储,那么就会导致额外的内存访问,从而影响程序的运行效率。
因此,遵守内存对齐原则能够减少内存访问次数,提高程序的运行速度。
在实际编程中,我们可以通过一些手段来保证数据的内存对齐。
比如,可以使用结构体来定义数据类型,结构体中的数据成员会按照定义的顺序依次存储在内存中,并且会根据最大数据类型的字节数进行对齐。
另外,编译器也会对数据进行自动对齐,以保证程序的正确性和效率。
除了结构体外,我们还可以使用#pragma pack指令来设置数据的对齐方式。
这个指令可以指定数据的对齐字节数,从而实现更灵活的内存对齐方式。
但是需要注意的是,过度使用#pragma pack可能会导致内存浪费,因此需要谨慎使用。
总的来说,内存对齐原则是计算机编程中一个非常重要的概念,它可以提高程序的运行效率,减少内存访问次数。
在编写程序时,我们应该遵守内存对齐原则,合理设计数据结构,以提高程序的性能和稳定性。
同时,我们也要注意内存对齐可能带来的内存浪费问题,尽量在保证效率的前提下减少内存的占用。
希望大家能够深入理解内存对齐原则,从而写出高效的程序,提升自己的编程水平。
内存对齐规则
内存对齐规则内存对齐是计算机系统中的一个重要概念,它指的是在内存中存储数据时,数据在内存中的起始地址必须是特定值的倍数。
这个特定值称为对齐单位。
内存对齐的存在是为了提高计算机系统的性能和效率。
本文将介绍内存对齐的规则和作用,并探讨其在计算机系统中的重要性。
一、内存对齐的规则在计算机系统中,内存对齐遵循以下规则:1. 基本对齐规则:数据的起始地址必须是其数据类型的整数倍。
例如,一个整型变量的起始地址必须是4的倍数,一个双精度浮点型变量的起始地址必须是8的倍数。
2. 结构体对齐规则:结构体中的成员变量按照其数据类型的对齐方式进行对齐。
结构体的起始地址必须是其成员变量中对齐要求最高的数据类型的整数倍。
3. 数组对齐规则:数组的起始地址必须是数组元素类型的对齐要求最高的数据类型的整数倍。
4. 结构体嵌套对齐规则:结构体嵌套时,内层结构体的起始地址必须是外层结构体中对齐要求最高的数据类型的整数倍。
二、内存对齐的作用内存对齐的主要作用是提高计算机系统的性能和效率。
具体而言,内存对齐可以带来以下好处:1. 提高访问速度:对齐的数据可以直接从内存中读取,而不需要进行额外的对齐操作。
这样可以减少内存访问的时间,提高程序的执行效率。
2. 节省内存空间:内存对齐可以使数据在内存中的布局更加紧凑,减少内存碎片的产生。
这样可以节省内存空间,提高内存的利用率。
3. 硬件兼容性:不同的硬件平台对内存对齐的要求可能不同。
遵循内存对齐规则可以增加程序在不同硬件平台上的兼容性,减少因为内存对齐问题而导致的程序错误。
三、内存对齐的重要性内存对齐在计算机系统中具有重要的意义。
首先,内存对齐可以提高程序的执行效率,减少内存访问的时间,提高计算机系统的性能。
其次,内存对齐可以减少内存碎片的产生,节省内存空间,提高内存的利用率。
此外,遵循内存对齐规则可以增加程序在不同硬件平台上的兼容性,提高程序的可移植性。
总结起来,内存对齐是计算机系统中的一个重要概念,它可以提高计算机系统的性能和效率。
内存对齐
内存对齐.txt2机会靠自己争取,命运需自己把握,生活是自己的五线谱,威慑呢们不亲自演奏好它?最近被面试了,打击挺大,问啥啥不会。
举一个很多不会的题中的一个,关于内存对齐的问题,以前也知道点,个人感觉很重要,在这里与同道中人分享下:很多书籍中都讲到:内存可以看成一个byte数组,我们通过编程语言提供的工具对这个'大数组'中的每个元素进行读写,比如在C中我们可以用指针一次读写一个或者更多个字节,这是我们一般程序员眼中的内存样子。
但是从机器角度更具体的说从CPU角度看呢,CPU发出的指令是一个字节一个字节读写内存吗?答案是'否'。
CPU是按照'块(chunk)'来读写内存的,块的大小可以是2bytes, 4bytes, 8bytes, 16bytes甚至是32bytes. 这个CPU访问内存采用的块的大小,我们可以称为'内存访问粒度'。
程序员眼中的内存样子:---------------------------------| | | | | | | | | | | | | | | | |---------------------------------0 1 2 3 4 5 6 7 8 9 A B C D E F (地址)CPU眼中的内存样子:(以粒度=4为例)---------------------------------------------| | | | | | | | | | | | | | | | | | | |---------------------------------------------0 1 2 3 4 5 6 7 8 9 A B C D E F (地址)有了上面的概念,我们来看看粒度对CPU访问内存的影响。
假设这里我们需要的数据分别存储于地址0和地址1起始的连续4个字节的存储器中,我们目的是分别读取这些数据到一个4字节的寄存器中,如果'内存访问粒度'为1,CPU从地址0开始读取,需要4次访问才能将4个字节读到寄存器中;同样如果'内存访问粒度'为1,CPU从地址1开始读取,也需要4次访问才能将4个字节读到寄存器中;而且对于这种理想中的''内存访问粒度'为1的CPU,所有地址都是'aligned address'。
内存对齐规则
内存对齐规则在计算机科学中,内存对齐是指将数据结构的起始地址设置为按照特定规则对齐的地址。
这个规则是为了优化内存的访问效率和提高计算机的性能。
下面将详细介绍内存对齐的规则以及它的作用。
1. 内存对齐的基本原则内存对齐的基本原则是将数据结构按照其大小进行对齐。
对齐的目的是为了保证数据结构的每个成员在内存中的地址都是对齐的,这样可以提高内存的读写效率。
通常情况下,数据结构的对齐方式与平台的硬件架构有关,如x86架构的对齐方式与ARM架构的对齐方式可能不同。
2. 内存对齐的规则内存对齐的规则是根据数据结构的大小来确定的。
以下是常见的内存对齐规则:- 字节对齐:数据结构的起始地址必须是其大小的整数倍。
例如,一个4字节大小的数据结构的起始地址必须是4的倍数。
- 短整型对齐:短整型数据结构的起始地址必须是2的倍数。
- 整型对齐:整型数据结构的起始地址必须是4的倍数。
- 长整型对齐:长整型数据结构的起始地址必须是8的倍数。
- 双精度浮点型对齐:双精度浮点型数据结构的起始地址必须是8的倍数。
3. 内存对齐的作用内存对齐可以提高计算机的性能和内存的访问效率。
首先,对齐的数据结构可以使计算机一次读取或写入多个连续的内存地址,减少了读写操作的次数,提高了内存访问的速度。
其次,对齐的数据结构可以减少内存碎片的产生,提高内存的利用率。
最后,对齐的数据结构可以避免由于内存对齐不当而引起的数据错误和性能下降。
4. 内存对齐的注意事项在进行内存对齐时,需要注意以下几点:- 结构体中的成员变量的声明顺序会影响内存的对齐方式。
通常情况下,将大小相同的成员变量放在一起可以减少内存的浪费。
- 在某些特殊情况下,可以使用特定的编译指令来控制内存对齐的方式,以满足特定的需求。
- 内存对齐可能会增加内存的消耗,特别是在数据结构中存在大量的填充字节的情况下。
因此,在设计数据结构时,需要权衡内存利用率和性能之间的关系。
总结起来,内存对齐是为了提高内存的读写效率和计算机的性能而进行的一种优化技术。
设置内存对齐的方法
设置内存对齐的⽅法
内存对齐是⾯试和嵌⼊式编程中常常出现的问题,这⾥介绍两种设置内存对齐的⽅法:
以这段代码为例:
#include "stdio.h"
struct example
{
short a;
int b;
double c;
char d;
char e;
};
void main(){
printf("example:%d\n",sizeof(example));
example st;
printf("%p\n",&st.a);
printf("%p\n",&st.b);
printf("%p\n",&st.c);
printf("%p\n",&st.d);
printf("%p\n",&st.e);
}
在不同的平台,即编译器环境中内存的对齐⽅式也不同,在笔者所⽤的vs2005中,默认的对齐是根据struct中长度最⼤的单元来进⾏。
往往为了减少内存对齐带来的额外内存损耗,需要对内存的对齐进⾏⼀些设置,常⽤的⽅法有两种:
1、编译器的设置
在vs2005中就提供了对内存对齐单位的设置,将设置量改为1,即内存不进⾏对齐处理,具体在项⽬ - 属性 - 配置属性 -
C/C++ - 代码⽣成 - 结构成员对齐中设置。
2、通过设置#pragma pack(n)来设定,假设结构体中长度最⼤的单元长为m,则按照min(m,n)来进⾏对齐。
结构体的内存空间分配原理
结构体的内存空间分配原理关于内存对齐⼀:1.什么是内存对齐假设我们同时声明两个变量:char a;short b;⽤&(取地址符号)观察变量a,b的地址的话,我们会发现(以16位CPU为例):如果a的地址是0x0000,那么b的地址将会是0x0002或者是0x0004。
那么就出现这样⼀个问题:0x0001这个地址没有被使⽤,那它⼲什么去了?答案就是它确实没被使⽤。
因为CPU每次都是从以2字节(16位CPU)或是4字节(32位CPU)的整数倍的内存地址中读进数据的。
如果变量b的地址是0x0001的话,那么CPU就需要先从0x0000中读取⼀个short,取它的⾼8位放⼊b的低8位,然后再从0x0002中读取下⼀个short,取它的低8位放⼊b的⾼8位中,这样的话,为了获得b的值,CPU需要进⾏了两次读操作。
但是如果b的地址为0x0002,那么CPU只需⼀次读操作就可以获得b的值了。
所以编译器为了优化代码,往往会根据变量的⼤⼩,将其指定到合适的位置,即称为内存对齐(对变量b做内存对齐,a、b之间的内存被浪费,a并未多占内存)。
2.结构体内存对齐规则结构体所占⽤的内存与其成员在结构体中的声明顺序有关,其成员的内存对齐规则如下:(1)每个成员分别按⾃⼰的对齐字节数和PPB(指定的对齐字节数,32位机默认为4)两个字节数最⼩的那个对齐,这样可以最⼩化长度。
(2)复杂类型(如结构)的默认对齐⽅式是它最长的成员的对齐⽅式,这样在成员是复杂类型时,可以最⼩化长度。
(3)结构体对齐后的长度必须是成员中最⼤的对齐参数(PPB)的整数倍,这样在处理数组时可以保证每⼀项都边界对齐。
(4)计算结构体的内存⼤⼩时,应该列出每个成员的偏移地址,则其长度=最后⼀个成员的偏移地址+最后⼀个成员数的长度+最后⼀个成员的调整参数(考虑PPB)。
下⾯举例说明上述规则:#include#pragma pack(2) //指定PPB为2struct T{char a; //偏移地址0int b; //偏移地址2char c; //偏移地址6};#pragma pack() //恢复原来默认PPB,32位下为4int main(int argc,char * argv[]){printf("sizeof(struct T));return0;}最后输出的结果为:8。
一篇文章带你了解C语言内存对齐公式
⼀篇⽂章带你了解C语⾔内存对齐公式⽬录⼀、前⾔⼆、公式2.1、例⼦⼀2.2、例⼦⼆2.3、例⼦三总结⼀、前⾔每⼀个特定平台上的编译器都有⾃⼰的默认“对齐系数”(也叫对齐模数)。
GCC中默认#program pack(4),即4个字节的内存对齐。
Keil也是采⽤4字节对齐的。
也可以通过预编译命令#pragma pack(n),n = 1,2,4,8,16来改变这⼀系数,⼀般情况下尽量使⽤⾃然对齐系数,不要修改它。
STM32单⽚机上各个变量占⽤的字节数:⼆、公式公式⼀、结构体变量⾥,成员的起始地址必须满⾜:起始地址 % 成员的字节数(sizeof值)= 0 (说⽩了就是能整除)公式⼆、结构体变量的总字节数必须满⾜:总字节数 % 最⼤的成员字节数 = 0 (说⽩了就是能整除)2.1、例⼦⼀struct te_a{/* 公式⼀ */char a; /* a的起始地址0x00,然后⽤公式⼀计算:0x00 % 1(char为1个字节) = 0,所以成员a占⽤了内存0x00 */int b; /* b的起始地址0x01 % 4(int为4个字节)不等于0,那么再计算0x02%4还是不等于0,直到0x04 % 4 = 0 ,所以成员b占⽤了内存0x04 ~ 0x07 */char c; /* 成员b的结尾地址是0x07,所以成员c从0x08开始计算,那么计算0x08 % 1 = 0 , 所以成员c占⽤了内存0x08 */}Test1;OK,经过公式⼀的运算后,结构体⾥成员的分布如下:经过公式⼀的计算后,结构体变量Test1的⼤⼩是9个字节。
内存对齐的计算还没有结束,接着使⽤公式⼆计算:结构体变量的总字节数 % 最⼤的成员字节数 = 0 ,在结构体变量Test1⾥,最⼤的成员是b,b的⼤⼩是4个字节。
那么,当前的结构体变量⼤⼩9字节 % 4字节等于 0 。
当结构体变量⼤⼩为12字节 % 4字节 = 0,所以最终结构体变量Test1占⽤的内存字节数是12,其内存的分布如下:以上的都是根据公式计算出来的结果,那实际在单⽚机⾥是不是这样呢?把代码下载到STM32单⽚机⾥,进⼊DEBUG模式看看。
内存地址对齐
内存地址对齐 内存地址对齐,是⼀种在计算机内存中排列数据、访问数据的⼀种⽅式,包含了两种相互独⽴⼜相互关联的部分:基本数据对齐和结构体数据对齐。
当今的计算机在计算机内存中读写数据时都是按字(word)⼤⼩块来进⾏操作的(在32位系统中,数据总线宽度为32,每次能读取4字节,地址总线宽度为32,因此最⼤的寻址空间为2^32=4GB,但是最低2位A[0],A[1]是不⽤于寻址,A[2-31]才能存储器相连,因此只能访问4的倍数地址空间,但是总的寻址空间还是2^30*字长=4GB,因此在内存中所有存放的基本类型数据的⾸地址的最低两位都是0,除结构体中的成员变量)。
基本类型数据对齐就是数据在内存中的偏移地址必须等于⼀个字的倍数,按这种存储数据的⽅式,可以提升系统在读取数据时的性能。
为了对齐数据,可能必须在上⼀个数据结束和下⼀个数据开始的地⽅插⼊⼀些没有⽤处字节,这就是结构体数据对齐。
举个例⼦,假设计算机的字⼤⼩为4个字节,因此变量在内存中的⾸地址都是满⾜4地址对齐,CPU只能对4的倍数的地址进⾏读取,⽽每次能读取4个字节⼤⼩的数据。
假设有⼀个整型的数据a的⾸地址不是4的倍数(如下图所⽰),不妨设为0X00FFFFF3,则该整型数据存储在地址范围为0X00FFFFF3~0X00FFFFF6的存储空间中,⽽CPU每次只能对4的倍数内存地址进⾏读取,因此想读取a的数据,CPU要分别在0X00FFFFF0和0X00FFFFF4进⾏两次内存读取,⽽且还要对两次读取的数据进⾏处理才能得到a 的数据,⽽⼀个程序的瓶颈往往不是CPU的速度,⽽是取决于内存的带宽,因为CPU得处理速度要远⼤于从内存中读取数据的速度,因此减少对内存空间的访问是提⾼程序性能的关键。
从上例可以看出,采取内存地址对齐策略是提⾼程序性能的关键。
举例:⾸先我们先看看下⾯的C语⾔的结构体:typedef struct MemAlign{int a;char b[3];int c;}MemAlign;以上这个结构体占⽤内存多少空间呢?也许你会说,这个简单,计算每个类型的⼤⼩,将它们相加就⾏了,以32为平台为例,int类型占4字节,char占⽤1字节,所以:4 + 3 + 4 = 11,那么这个结构体⼀共占⽤11字节空间。
如何通过内存对齐提高程序性能(六)
如何通过内存对齐提高程序性能一、引言在计算机科学领域,程序性能优化是一个永恒的话题。
通过合理的内存对齐,可以大幅提高程序的执行效率。
本文将讨论如何通过内存对齐来优化程序性能,以提高计算机系统的响应能力和运行效率。
二、内存对齐的概念内存对齐是指数据存储在内存中的起始地址是否是其大小的整数倍。
例如,一个4字节的整型变量在内存中的地址如果是4的整数倍,则称为对齐;否则称为不对齐。
内存对齐可以提高计算机的内存读写效率,加快程序的执行速度。
三、内存对齐的原理内存对齐的原理是将数据存放在连续的内存块中,使得每个数据元素的起始地址都是其大小的整数倍。
这样可以避免因为读取非对齐数据而产生的性能损失。
在现代计算机架构中,对齐数据的访问速度要快于不对齐数据。
四、内存对齐带来的性能优势1. 提高内存读写效率:对齐数据的读取是按照内存块进行,而不对齐数据则需要进行额外的处理,包括拆分、合并等操作,这无疑会增加内存读写的开销。
2. 编译器优化:在编译器优化过程中,对齐数据可以更好地进行向量化操作,提高计算密集型程序的执行效率。
3. 提高缓存命中率:CPU缓存是按照缓存行(Cache Line)进行数据读取的,对齐数据有更高的命中率,减少了数据在缓存中的拆分和合并操作。
五、如何进行内存对齐1. 结构体对齐:在声明结构体时,可以使用编译器提供的对齐宏指令,如#pragma pack(n)(n为对齐字节数)。
合理选择结构体成员的顺序和大小,可以优化内存对齐。
2. 优化数组内存对齐:在数组的声明中,可以使用对齐宏指令来指定数组的对齐方式。
在遍历数组时,也应注意对齐方式,避免不对齐读取。
3. 内存对齐的影响:内存对齐可能会增加内存的占用空间,但是在性能优化的前提下,这种增加是可以接受的。
六、内存对齐的案例分析为了更好地说明内存对齐的性能优势,我们可以通过一个实例来进行案例分析。
假设有一个结构体如下所示:```struct {char a;int b;double c;}```在不对齐的情况下,结构体的大小为12字节。
结构体对齐规则
结构体对齐规则1、什么是内存对齐?我们都知道,定义的变量(元素)是要按照顺序一个一个放到内存中去的,它们也不一定就是紧密排列的,是要按照一定的规则就行排放的,这就是内存对齐。
对结构体来说,元素的存储从首地址开始,第一个元素的地址和整个结构体的首地址相同,其他的每个元素放置到内存中时,它都会认为内存是按照元素自己的大小来划分空间的,所以元素放置在内存中的位置一定会在元素自己宽度(字节数)的整数倍上开始,这就是所谓的结构体内存对齐问题。
特别有意思的是,C语言同意使用者自行确定内存对齐的设置,通过伪指令#pragma pack (n) 可以重新设定内存对齐的字节数。
这个后面会讲到!2、为什么要有内存对齐?这真是一个好问题!从网上了解到的几个原因:(1)考虑平台的原因。
实际的硬件平台跑代码是有所区别的,一些硬件平台可以对任意地址上的任意数据进行访问,而有一些硬件平台就不行,就是有限制,所以内存对齐是一种解决办法。
(2)考虑性能的原因。
CPU访问内存时,如果内存不对齐的话,为了访问到数据的话就需要几次访问,而对齐的内存只需要访问一次即可,提高了CPU访问内存的速度。
3、结构体的内存对齐规则是什么?每当有用到结构体的时候,总会考虑这个结构体实际应该要占用多少的内存,是否还有优化的空间。
特别是在面试时,结构体的内存对齐问题是很多面试会考到,也会经常被提及问起,属于高频考点了!话不多说,直接奉上结构体的内存对齐的判别方法,方便大家快速算出结构体所占的内存大小。
这里先规定一下:内存对齐值称为内存对齐有效值,这个值可以是1、2、4、8、16,所以先规定一下。
规则:规则1,结构体第一个成员一定是放在结构体内存地址里面的第1位。
规则2,成员对齐规则:除了第一个成员,之后的每个数据成员的对齐要按照成员自身的长度和内存对齐有效值进行比较,按两者中最小的那个进行对齐,即偏移的倍数。
规则3,结构体整体对齐规则:数据成员完成对齐之后,对整个结构体的大小进行对齐。
大端和小端--内存对齐问题
⼤端和⼩端--内存对齐问题什么是⼤端和⼩端Big-Endian和Little-Endian的定义如下:1) Little-Endian就是低位字节排放在内存的低地址端,⾼位字节排放在内存的⾼地址端。
2) Big-Endian就是⾼位字节排放在内存的低地址端,低位字节排放在内存的⾼地址端。
举⼀个例⼦,⽐如数字0x12 34 56 78在内存中的表⽰形式为:1)⼤端模式:低地址 -----------------> ⾼地址0x12 | 0x34 | 0x56 | 0x782)⼩端模式:低地址 ------------------> ⾼地址0x78 | 0x56 | 0x34 | 0x12可见,⼤端模式和字符串的存储模式类似。
3)下⾯是两个具体例⼦:16bit宽的数0x1234在Little-endian模式(以及Big-endian模式)CPU内存中的存放⽅式(假设从地址0x4000开始存放)为:内存地址⼩端模式存放内容⼤端模式存放内容0x40000x340x120x40010x120x3432bit宽的数0x12345678在Little-endian模式以及Big-endian模式)CPU内存中的存放⽅式(假设从地址0x4000开始存放)为:内存地址⼩端模式存放内容⼤端模式存放内容0x40000x780x120x40010x560x340x40020x340x560x40030x120x784)⼤端⼩端没有谁优谁劣,各⾃优势便是对⽅劣势:⼩端模式:强制转换数据不需要调整字节内容,1、2、4字节的存储⽅式⼀样。
⼤端模式:符号位的判定固定为第⼀个字节,容易判断正负。
数组在⼤端⼩端情况下的存储: 以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以⽤unsigned char buf[4]来表⽰value: Big-Endian: 低地址存放⾼位,如下:⾼地址---------------buf[3] (0x78) -- 低位buf[2] (0x56)buf[1] (0x34)buf[0] (0x12) -- ⾼位---------------低地址Little-Endian: 低地址存放低位,如下:⾼地址---------------buf[3] (0x12) -- ⾼位buf[2] (0x34)buf[1] (0x56)buf[0] (0x78) -- 低位--------------低地址为什么会有⼤⼩端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着⼀个字节,⼀个字节为8bit。
内存对齐规则
输出结果:sizeof(struct test_t) = 10 [两个编译器输出一致]
分析过程:
1)成员数据对齐
#pragma pack(2)
struct test_t {
int a;
char b;
short c;
char d;
};
#pragma pack()
分析过程:
1)成员数据对齐
#pragma pack(1)
struct test_t {
int a;
char b;
short c;
char d;
};
#pragma pack()
成员总大小=8
2)整体对齐
整体对齐系数= min((max(int,short,char), 1) = 1
整体大小(size)=$(成员总大小)按$(整体对齐系数)圆整= 8 [注1]
最常见的就是struct数据结构的sizeof的结果出乎意料。
结构体的sizeof的值并不是简单的将其中各个元素所占的字节相加,而是要考虑到存储空间的字节对齐问题
结构体默认的字节对齐准则:
1.结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2.结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字符;
成员总大小=9
2)整体对齐
整体对齐系数= min((max(int,short,char), 2) = 2
整体大小(size)=$(成员总大小)按$(整体对齐系数)圆整= 10
3、4字节对齐(#pragma pack(4))
输出结果:sizeof(struct test_t) = 12 [两个编译器输出一致]
内存对齐(结构体和Union)
}AA;
int main()
{
AA a;
cout<<sizeof(a)<<" "<<sizeof(BB)<<endl;
return 0;
}
结果是
48 24
ok,上面的全看明白了,内存对齐基本过关.
明白了不?
那#pragma pack(2)的结果又是多少呢?对不起,5分钟到了,自己去测试吧.
===============================================================
一会搞定union内存字节对齐
也是转载一个论坛的回复:
其实union(共用体)的各个成员是以同一个地址开始存放的,每一个时刻只可以存储一个成员,这样就要求它在分配内存单元时候要满足两点:
等你看完此3条原则,2分钟已经过去,抓紧时间,实战3分钟:
typedef struct bb
{
int id; //[0]....[3]
double weight; //[8].....[15] 原则1
float height; //[16]..[19],总长要为8的整数倍,补齐[20]...[23] 原则3
char a;//元长度1
int b[5];//元长度4
double c;//元长度8
int d[3];
};
本来mm的空间应该是sizeof(int)*5=20;但是如果只是20个单元的话,那可以存几个double型(8位)呢?两个半?当然不可以,所以mm的空间延伸为既要大于20,又要满足其他成员所需空间的整数倍,即24
内存对齐的目的和原理
目的:
1. 提高内存访问效率。
由于CPU在读取内存时,一次性会以固定长度(如4个字节、8个
字节等)读取数据,而不是一个一个的字节读取。
如果将多个变量连续分配在内存中而不
进行对齐处理的话,就会造成CPU在读取数据时出现“半包”情况。
此时CPU必须要做2
次内存访问才能得到所有的数据,显然效率低了很多。
2. 减少内存占用量。
当使用对齐方式来分布各个变量时,有些余留出来的空间是浪费的。
但是通过使用对其方式来分布各个变量也能避免上文中所说的“半包”情况出现从而大大
减少 CPU 读取数据所浪费的时间和功耗
原理:
1. 对齐原理是将物理地址向上舍入到最近的能被对齐因子(Alignment Factor) 整除的
倍数,例如 4 字(32bit) 要 4 字(32bit) 对齐, 8 字 (64bit) 要 8 字 (64bit) 对齐;
2. 由于 CPU 大部分都是 32 bit 或 64 bit 系统,因此通常要 4 byte 或 8 byte 相应
地对齐;
3. 有些 CPU 要求 16 byte 的对齐,例如 Intel Pentium Pro/II/III/IV ,AMD K6-2/K7 Athlon ;
4. 有些 CPU 要 32 byte 的对齐 ,例如 AMD Opteron / Athlon 64 .。
内存对齐的理解
内存对齐的理解
内存对齐是一种优化技术,其目的是在存储单元大小为N的计算机上,使数据结构的首地址为N的倍数。
这样可以提高访问内存的效率,从而提高程序的性能。
在C/C++语言中,结构体和类的成员变量是按照定义的顺序依次存放在内存中的。
但是,由于计算机硬件的限制,存储单元的大小通常不是任意大小,而是固定的,如8字节、4字节、2字节等。
这时,如果结构体或类的成员变量大小不是存储单元大小的整数倍,就会出现内存对齐问题。
内存对齐的规则是,将结构体或类的成员变量按照从大到小的顺序排序,然后按照存储单元大小的整数倍进行对齐。
具体来说,如果某个成员变量的大小小于存储单元大小,则在其后面填充空白字节,使其占用的空间大小为存储单元大小的整数倍。
如果某个成员变量的大小等于存储单元大小,则不需要进行对齐。
如果某个成员变量的大小大于存储单元大小,则需要将其拆分成多个存储单元大小的部分进行对齐。
内存对齐的优点是可以提高程序的性能,因为CPU在处理内存时通常是以存储单元大小为单位进行读写的,如果数据结构的首地址不是存储单元大小的整数倍,就需要进行多次读写操作,这会浪费一定的时间和资源。
而进行内存对齐后,CPU可以一次读写整个存储单元,从而提高了程序的效率。
值得注意的是,内存对齐不仅仅是在结构体和类的成员变量中存
在,还可以在函数的调用过程中存在。
在函数调用时,参数的传递也需要进行内存对齐,以保证程序的正确性和性能。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
最近被面试了,打击挺大,问啥啥不会。
举一个很多不会的题中的一个,关于内存对齐的问题,以前也知道点,个人感觉很重要,在这里与同道中人分享下:
很多书籍中都讲到:内存可以看成一个byte数组,我们通过编程语言提供的工具对这个'大数组'中的每个元素进行读写,比如在C中我们可以用指针一次读写一个或者更多个字节,这是我们一般程序员眼中的内存样子。
但是从机器角度更具体的说从CPU角度看呢,CPU发出的指令是一个字节一个字节读写内存吗?答案是'否'。
CPU是按照'块(chunk)'来读写内存的,块的大小可以是2bytes, 4bytes, 8bytes, 16bytes甚至是32bytes. 这个CPU访问内存采用的块的大小,我们可以称为'内存访问粒度'。
程序员眼中的内存样子:
---------------------------------
| | | | | | | | | | | | | | | | |
---------------------------------
0 1 2 3 4 5 6 7 8 9 A B C D E F (地址)
CPU眼中的内存样子:(以粒度=4为例)
---------------------------------------------
| | | | | | | | | | | | | | | | | | | |
---------------------------------------------
0 1 2 3 4 5 6 7 8 9 A B C D E F (地址)
有了上面的概念,我们来看看粒度对CPU访问内存的影响。
假设这里我们需要的数据分别存储于地址0和地址1起始的连续4个字节的存储器中,我们目的是分别读取这些数据到一个4字节的寄存器中,
如果'内存访问粒度'为1,CPU从地址0开始读取,需要4次访问才能将4个字节读到寄存器中;
同样如果'内存访问粒度'为1,CPU从地址1开始读取,也需要4次访问才能将4个字节读到寄存器中;而且对于这种理想中的''内存访问粒度'为1的CPU,所有地址都是'aligned address'。
如果'内存访问粒度'为2,CPU从地址0开始读取,需要2次访问才能将4个字节读到寄存器中;每次访存都能从'aligned address'起始。
如果'内存访问粒度'为2,CPU从地址1开始读取,相当于内存中数据分布在1,2,3,4三个地址上,由于1不是'aligned address',所以这时CPU要做些其他工作,由于这四个字节分步在三个chunk上,所以CPU需要进行三次访存操作,第一次读取chunk1(即地址0,1上两个字节,而且仅仅地址1上的数据有用),第二次读取chunk2(即地址2,3上两个字节,这两个地址上的数据都有用),最后一次读取chunk3(即地址5,6上两个字节,而且仅仅地址5上的
数据有用),最后CPU会将读取的有用的数据做merge操作,然后放到寄存器中。
同理可以推断如果'内存访问粒度'为4,那么从地址1开始读取,需要2次访问,访问后得到的结果merge后放到寄存器中。
是不是所有的CPU都会帮你这么做呢,当然不是。
有些厂商的CPU发现你访问unaligned address,就会报错,或者打开调试器或者dump core,比如sun sparc solaris绝对不会容忍你访问unaligned address,都会以一个core结束你的程序的执行。
所以一般编译器都会在编译时做相应的优化以保证程序运行时所有数据都是存储在'aligned address'上的,这就是内存对齐的由来。
我们可以指定按照何种粒度访问特定内存块儿:其中void *T为指向特定内存块的地址指针char *p = (char*)T;每次操作一个字节
short *p = (short*)T;每次操作两个字节
int *p = (int*)T;每次操作4个字节
以此类推。
在'Data alignment: Straighten up and fly right'这篇文章中作者还得出一个结论那就是:"如果访问的地址是unaligned的,那么采用大粒度访问内存有可能比小粒度访问内存还要慢"。
我试验用的编译器包括GCC 3.4.2和VC6.0的C编译器,平台为Windows XP + Sp2。
我们将用典型的struct对齐来说明。
首先我们定义一个struct:
#pragma pack(n)
struct test_t {
int a;
char b;
short c;
char d;
};
#pragma pack(n)
首先我们首先确认在试验平台上的各个类型的size,经验证两个编译器的输出均为:
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
我们的试验过程如下:通过#pragma pack(n)改变“对齐系数”,然后察看sizeof(struct test_t)的值。
1、1字节对齐(#pragma pack(1))
输出结果:sizeof(struct test_t) = 8 [两个编译器输出一致]
分析过程:
1) 成员数据对齐
#pragma pack(1)
struct test_t {
int a;
char b;
short c;
char d;
};
#pragma pack()
成员总大小=8
2) 整体对齐
整体对齐系数 = min((max(int,short,char), 1) = 1
整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 8 [注1]
2、2字节对齐(#pragma pack(2))
输出结果:sizeof(struct test_t) = 10 [两个编译器输出一致]
分析过程:
1) 成员数据对齐
#pragma pack(2)
struct test_t {
int a;
char b;
short c;
char d;
};
#pragma pack()
成员总大小=10
2) 整体对齐
整体对齐系数 = min((max(int,short,char), 2) = 2
整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 10
3、4字节对齐(#pragma pack(4))
输出结果:sizeof(struct test_t) = 12 [两个编译器输出一致]
分析过程:
1) 成员数据对齐
#pragma pack(4)
struct test_t {
int a;
char b;
short c;
char d;
};。