栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C/C++ 内存对齐

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C/C++ 内存对齐

目录

什么是内存对齐为什么需要内存对齐数据类型大小内存对齐规则样例

例子1:最大类型int例子2:最大类型double例子3:指定一字节对齐值例子4:指定二字节对齐值样例5:结构体类型数据成员 什么情况下需要内存对齐

什么是内存对齐

为了提高程序的性能,数据结构应该尽可能地在自然边界上对齐。

为什么需要内存对齐

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

2、提高处理器访问速度:对于未对齐的内存,处理器可能需要访问两次内存才能将数据完全读出,而对于对齐的内存,处理器只需要一次即可。

对于原因2具体解释一下:

尽管内存是以字节为单位,但是大部分CPU并不是按字节块来存取内存的。它一般会以2的n次方个字节为单位来存取内存,将上述这些存取单位称为内存存取粒度。

假设我们的内存存取粒度为4,即CPU只能从地址为4的倍数的内存开始读取数据。 对于int类型的数据,其占4个字节。


如果没有内存对齐,该数据可能存放在从地址1开始的内存中,即【1-4】,那么处理器需要先读取从地址0开始的连续4个内存单元【0-3】,再读取从地址4开始的连续4个内存单元【4-7】,总共需要CPU访问两次内存才能完全获取该int类型的值。

如果有内存对齐,该数据只能存放在地址为4的倍数的内存单元中,比如其在【0-3】,那么只需要读取从地址0开始的连续4个内存单元【0-3】,即CPU访问一次内存就能完全获取该类型的值

数据类型大小

在32位编译器中

char:1个字节

short:2个字节

int:4个字节

指针:4个字节(在64位编译器中是8个字节)

float:4个字节

long:4个字节(在64位编译器中是8个字节)

double:8个字节

long long:8个字节

内存对齐规则

可以通过预编译命令#pragma pack(n)来指定有效对齐值

注意:并不是说n就是有效对齐值,可以理解为建议以n为有效对齐值,实际有效对齐值还要根据结构体成员大小来决定,如果n的值比结构体数据成员的大小小才起作用。

有效对齐值N:表示地址对齐在N上,即数据的存放地址%N=0;

如果没有预编译命令#pragma pack(n)指定有效对齐值, 则每个特定平台上的编译器都有自己的默认值n,通常Linux默认值为4,window默认值为8

结构体数据成员对齐规则:第一个成员放在offet(偏移量)为0的地方,以后每个数据成员的offset按照该成员的大小和有效对齐值中较小的整数倍,即

有 效 对 齐 值 = m i n ( 数 据 成 员 类 型 大 小 , n ) o f f s e t = 有 效 对 齐 值 ∗ 整 数 倍 有效对齐值=min(数据成员类型大小,n)\ offset=有效对齐值*整数倍 有效对齐值=min(数据成员类型大小,n)offset=有效对齐值∗整数倍

结构体对齐规则:在数据成员对齐后,结构体本身也要对齐,其总大小为有效对齐值的整数倍,即
有 效 对 齐 值 = m i n ( 数 据 成 员 最 大 类 型 大 小 , n ) s i z e o f ( 结 构 体 ) = 有 效 对 齐 值 ∗ 整 数 倍 有效对齐值=min(数据成员最大类型大小,n)\ sizeof(结构体)=有效对齐值*整数倍 有效对齐值=min(数据成员最大类型大小,n)sizeof(结构体)=有效对齐值∗整数倍

结构体作为数据成员:对于数据成员是结构体的情况,则该结构体成员要从其内部最大数据成员大小的整数倍地址开始存储

样例 例子1:最大类型int

上面提到默认情况下,window的n为8,而最大类型为int,所以结构体按照min(n,4)=4字节对齐
实际的内存分布图如下:

为了减少篇幅,后面的样例数据的分布图都将用上面示意图呈现


struct S1 {
	char c;//类型长度1<8按1字节对齐;offset为0,存放区间在[0,0]
	int i;//类型长度4<=8按4字节对齐;offset为4,存放区间在[4,7]
	short s;//类型长度2<8按2字节对齐;offset为8,存放区间在[8,9]
};//进行结构体对齐,将10(9-0+1)提升到4的倍数,最终该结构体大小为12

struct S2 {
	char c;//类型长度1<8按1字节对齐;offset为0,存放区间在[0,0]
	short s;//类型长度2<8按2字节对齐;offset为2,存放区间在[2,3]
	int i;//类型长度4<=8按4字节对齐;offset为4,存放区间在[4,7]
};//进行整体对齐,将8提升到4的倍数,最终该结构体大小为8


int main()
{
	S1 objectS1 = { 'a','1','2' };
	S2 objectS2 = { 'a','2','1' };
	cout << "sizeof S1:" << sizeof(objectS1) << endl;//输出12
	cout << "sizeof S2:" << sizeof(objectS2) << endl;//输出8
}
例子2:最大类型double

默认情况下,最大类型为double,所以结构体按照min(n,8)=8字节对齐

struct S1 {
	char c;//类型长度1<8按1字节对齐;offset为0,存放区间在[0,0]
	double d;//类型长度8<=8按8字节对齐;offset为8,存放区间在[8,15]
	short s;//类型长度2<8按2字节对齐;offset为16,存放区间在[16,7]
};//进行整体对齐,将17提升到8的倍数,最终该结构体大小为24
struct S2 {
	char c;//类型长度1<8按1字节对齐;offset为0,存放区间在[0,0]
	short s;//类型长度2<8按2字节对齐;offset为2,存放区间在[2,3]
	double d;//类型长度8<=8按4字节对齐;offset为8,存放区间在[8,15]
};//进行整体对齐,将16提升到8的倍数,最终该结构体大小为16


int main()
{
	S1 objectS1 = { 'a','1','2' };
	S2 objectS2 = { 'a','2','1' };
	cout << "sizeof S1:" << sizeof(objectS1) << endl;//输出24
	cout << "sizeof S2:" << sizeof(objectS2) << endl;//输出16
}
例子3:指定一字节对齐值

用#pragma pack(1)指定一字节对齐值

#pragma pack(1)
struct S1 {
	char c;//类型长度1<=1按1字节对齐;offset为0,存放区间在[0,0]
	int i;//类型长度4>1按1字节对齐;offset为1,存放区间在[1,4]
	short s;//类型长度2>1按1字节对齐;offset为5,存放区间在[5,6]
};//类型进行结构体对齐,将7提升到1的倍数,最终该结构体大小为7
int main()
{
	S1 objectS1 = { 'a','1','2' };
	cout << "sizeof S1:" << sizeof(objectS1) << endl;//输出7
}
例子4:指定二字节对齐值

用#pragma pack(2)指定二字节对齐值

#pragma pack(2)
struct S1 {
	char c;//类型长度1<2按1字节对齐;offset为0,存放区间在[0,0]
	int i;//类型长度4>2按2字节对齐;offset为2,存放区间在[2,5]
	short s;//类型长度2>=2按2字节对齐;offset为6,存放区间在[6,7]
};//进行结构体对齐,将8提升到2的倍数,最终该结构体大小为8

int main()
{
	S1 objectS1 = { 'a','1','2' };
	cout << "sizeof S1:" << sizeof(objectS1) << endl;//输出8
}
样例5:结构体类型数据成员

#pragma pack(4)
struct S1 {
	char c;//类型长度1<4按1字节对齐;offset为0,存放区间在[0,0]
	double d;//类型长度8>4按4字节对齐;offset为4,存放区间在[4,11]
	short s;//类型长度2<4按2字节对齐;offset为12,存放区间在[12,13]
};//进行整体对齐,将14提升到4的倍数,最终该结构体大小为16

struct S2 {
	char c;//类型长度1<4按1字节对齐;offset为0,存放区间在[0,0]
	double s;//类型长度8>4按4字节对齐;offset为4,存放区间在[4,11]
	S1 m_S1;//该结构体内最大数据成员类型是double,所以可以把S1看成double来计算,但8>4,所以按4字节对齐;offset为12,存放区间在[12,27]
	short arr[3];//类型长度2<4按2字节对齐;offset为28,存放区间在[28,33]
};//进行整体对齐,将34提升到4的倍数,最终该结构体大小为36


int main()
{
	S1 objectS1 = { 'a','1','2' };
	S2 objectS2 = { 'a','2',objectS1,{1,2,3} };
	cout << "sizeof S1:" << sizeof(objectS1) << endl;//输出16
	cout << "sizeof S2:" << sizeof(objectS2) << endl;//输出36
}

注意:结构体中的结构体类型的成员变量也要进行整体对齐

什么情况下需要内存对齐
    该数据需要直接写入文件该数据需要通过网络传给其他程序

参考:

C/C++内存对齐详解

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/783819.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号