C++中的内存对齐

一、各变量类型占用内存:
char:1个字节

wchar_t:2个字节

bool:1个字节

short:2个字节

int:4个字节

float:4个字节

double:8个字节

long long:8个字节

32位系统:

long:4个字节

指针:4个字节

64位系统:

long:8个字节

指针:8个字节

unsigned不影响sizeof的取值。

对函数使用sizeof,再编译阶段会被函数返回值的类型取代,如int f1()会返回4,double f2()会返回8.

二、数组占用内存

数组大小就是各维数的乘积*数组元素的大小。

char a[] = "abcdef";  
int b[20] = {3, 4};  
char c[2][3] = {"aa", "bb"};  
  
cout<<sizeof(a)<<endl; // 7  
cout<<sizeof(b)<<endl; // 20*4=80  
cout<<sizeof(c)<<endl; // 6  

数组a的大小在定义时未指出,编译时分配给它的空间按照初始化的值确定,未元素个数加1,为7;

b为分配的20个元素*4=80;

c为2*3=6

看这一段程序:

int *d = new int[10];
cout << sizeof(d) << endl;

d是动态数组,它实质上是一个指针,所以sizeof(d)的值为4. 

三、结构体占用内存

对齐: 

 现代计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐

为什么需要内存对齐?

1.不同硬件平台不一定支持访问任意内存地址数据,使用内存对齐可以保证每次访问都从块内存地址头部开始存取

2.提高cpu内存访问速度,内存是分块的,如两字节一块,四字节一块,考虑这种情况:一个四字节变量存在一个四字节地址的后三位和下一个四字节地址的前一位,这样cpu从内存中取数据便需要访问两个内存并将他们组合起来,降低cpu性能

用空间换时间

能否用memcmp对两个结构体内存进行比较?

不可以,memcmp是逐个字节对比的,但当字节对齐时,中间的填充部分是随机的,不确定的地址,所以比较的结果是不正确的。

 结构体字节对齐的细节和具体编译器实现相关,但一般而言满足三个准则:

     1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

     2) 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);

     3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节{trailing padding}。

例1:

结构体定义如下:

struct A

{

    char a;

    short b;

    int c;

};
a占用一个字节之后,为使b满足2要求,需要再a之后填充一个字节。总的占用为8字节内存。

例2:

结构体定义如下:

struct A

{

   short b;

   int c;

   char a;

};

为了使c满足第二条准则,需要在给b填充两个字节之后再次填充两个字节。同时为了满足第三条准则,需要在给a填充一个字节之后再在后方填充三个字节。总占用内存为12个字节。

sizeof(T_Test)为16;

char a占用1个字节,

short b占用2个字节,在a与b之间填充1个字节

char c占用1个字节

int d占用4个字节,在c与d之间填充3个字节

char e[3]占用3个字节,之后补充1个字节,

sizeof(T_Test)共16个字节。

例4:

有指定时其对齐的规则是:每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐,并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。

程序如下:

#pragma pack(2) //指定按2字节对齐

struct C

{

   char b;

   int a;

   short c;

};

#pragma pack() //取消指定对齐,恢复缺省对齐

分析:

b占用1个字节

a占用4个字节,大于2,分成两个2字节,在b与a之间填充1个字节,c占用2个字节,一共8个字节。

sizeof(struct C) = 8.

添加一个后来看到的结构体里嵌套结构体的sizeof大小求解问题:

struct stu
{
	int i;
	struct
	{
		char c;
		int j;
	}ss;
	int k;
};
cout << sizeof(stu) << endl;

struct stu
{
	float f;
	char p;
	int adf[3];
};
cout << sizeof(stu) << endl;

输出结果为20,同嵌套结构体一样,确定最大占用空间类型时将数组拆分开,而不是用整个数组占用空间大小作为标准,

所以最大为int或float的4位,4+4+4*3=20。

四、类占用内存

1.

sizeof空类大小为1.

2.

class A
{
public:
        int a;
private:
	char b;
};
cout << sizeof(A) << endl;

 五、其他  union的sizeof

***************************************分割线*************************************************************

另一个版本说明

 运算符sizeof可以计算出给定类型的大小,对于32位系统来说,sizeof(char) = 1; sizeof(int) = 4。基本数据类型的大小很好计算,我们来看一下如何计算构造数据类型的大小。       

       C语言中的构造数据类型有三种:数组、结构体和共用体。

       数组是相同类型的元素的集合,只要会计算单个元素的大小,整个数组所占空间等于基础元素大小乘上元素的个数。

       结构体中的成员可以是不同的数据类型,成员按照定义时的顺序依次存储在连续的内存空间。和数组不一样的是,结构体的大小不是所有成员大小简单的相加,需要考虑到系统在存储结构体变量时的地址对齐问题。看下面这样的一个结构体:

struct stu1
{
     int i;
     char c;
     int j;
};

      先介绍一个相关的概念——偏移量。偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。显然,结构体变量中第一个成员的地址就是结构体变量的首地址。因此,第一个成员i的偏移量为0。第二个成员c的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;第三个成员j的偏移量是第二个成员的偏移量加上第二个成员的大小(4+1),其值为5。

      然而,在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:

      (1)结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍) 

      (2)结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数

      上面的例子中前两个成员的偏移量都满足要求,但第三个成员的偏移量为5,并不是自身(int)大小的整数倍。编译器在处理时会在第二个成员后面补上3个空字节,使得第三个成员的偏移量变成8。结构体大小等于最后一个成员的偏移量加上其大小,上面的例子中计算出来的大小为12,满足要求。

       再来看另外一个例子:

struct stu2
{
      int k;
      short t;
};

   成员k的偏移量为0;成员t的偏移量为4,都不需要调整。但计算出来的大小为6,显然不是成员k大小的整数倍。因此,编译器会在成员t后面补上2个字节,使得结构体的大小变成8从而满足第二个要求。

       由此可见,结构体类型需要考虑到字节对齐的情况,不同的顺序会影响结构体的大小。

       对比下面两种定义顺序:

struct stu3
{ 
       char c1; 
       int i;
       char c2;
}
struct stu4
{
       char c1;
       char c2;
       int i;
 }

     虽然结构体stu3和stu4中成员都一样,但sizeof(struct stu3)的值为12而sizeof(struct stu4)的值为8。

       对于嵌套的结构体,需要将其展开。对结构体求sizeof时,上述两种原则变为:

       (1)展开后的结构体的第一个成员的偏移量应当是被展开的结构体中最大的成员的整数倍。

       (2)结构体大小必须是所有成员大小的整数倍,这里所有成员计算的是展开后的成员,而不是将嵌套的结构体当做一个整体

       看下面的例子:

struct stu5
{
      short i;
      struct 
      {
           char c;
           int j;
      } ss; 
      int k;
}

   结构体stu5的成员ss.c的偏移量应该是4,而不是2。整个结构体大小应该是16。

      下述代码测试原则2:

struct stu5
{
      char i;
      struct 
      {
           char c;
           int j;
      } ss; 
      char a;
      char b;
      char d;
      char e;
      char f;
}

  结构体ss单独计算占用空间为8,而stu5的sizeof则是20,不是8的整数倍,这说明在计算sizeof(stu5)时,将嵌套的结构体ss展开了,这样stu5中最大的成员为ss.j,占用4个字节,20为4的整数倍。如果将ss当做一个整体,结果应该是24了。

       另一个特殊的例子是结构体中包含数组,其sizeof应当和处理嵌套结构体一样,将其展开,如下例子:

struct ss
{
    float f;
    char p;
    int adf[3];
};
cout<<sizeof(ss)<<endl;

   其值为20。float占4个字节,到char p时偏移量为4,p占一个字节,到int adf[3]时偏移量为5,扩展为int的整数倍,而非int adf[3]的整数倍,这样偏移量变为8,而不是12。结果是8+12=20,是最大成员float或int的大小的整数倍。

       如何给结构体变量分配空间由编译器决定,以上情况针对的是Linux下的GCC。在Windows下的VC平台也是这样,至于其他平台,可能会有不同的处理。

sizeof浅析(一)——求结构体大小_舒夜无痕的博客-CSDN博客_sizeof结构体

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值