如何计算对象或者类型所占的内存字节数(各种总结)

来源:互联网 发布:网吧计费软件破解 编辑:程序博客网 时间:2024/05/01 00:19

总结一下将sizeof用于各种类型对象或类型时其结果的算法。

1、认识sizeof

sizeof是C/C++中的一个operator,其作用就是返回一个对象或者类型所占的内存字节数(byte)。返回值是size_t,在头文件stddef.h中定义。

2、用法

sizeof的用法有一下3种:

(1)sizeof(object);//即sizeof(对象)

(2)sizeof(type_name);//即sizeof(类型名,如int,char...etc)

(3)sizeof object;//即sizeof 对象

例如:

[cpp] view plaincopyprint?
  1. int i;  
  2. sizeof(i);  
  3. sizeof(int);  
  4. sizeof i;  
  5. sizeof int//error!!!  

【注】:

(1)实际上sizeof计算对象的大小也是转换成对象类型来计算的,that is to say:对同类型的不同对象使用sizeof操作,得到的值是一样的。

(2)关于上面第(1)个表达式中的object,其可以延伸至一个unary-expression(一元表达式),例如:

[cpp] view plaincopyprint?
  1. sizeof(2); //相当于sizeof(int)  
  2. sizeof(2+4.55); //相当于sizeof(2.0+4.55),从而相当于sizeof(double)  

(3)sizeof可以对函数调用求值,就相当于对该函数的返回值类型求sizeof。

[cpp] view plaincopyprint?
  1. char foo()  
  2. {  
  3.     cout<<"foo函数被调用!"<<endl;  
  4.     return 'a';  
  5. }//函数   
  6. int foo_size;  
  7. foo_size=sizeof(foo());//相当于求sizeof(char)  

(4)sizeof的计算发生在编译时刻,所以它可以被当作常量表达式使用。

例如:

[cpp] view plaincopyprint?
  1. char ary[ sizeofint ) * 10 ];  //ok~~~  

3、各种sizeof计算

(1)基本数据类型的sizeof(C++primer中叫内置类型),如:int,char,double,float,short,long

在32位计算机中,各种内置类型所占的字节数如下:

char-------------1bytes;

short------------2bytes;

int----------------4bytes;

long--------------4bytes;

float--------------4bytes;

double-----------8bytes;

long double----8bytes;

(2)指针数据类型的sizeof

指针记录了另一个对象的地址,即指针变量是用来存放地址数据的,那么其当然等于计算机地址总线的宽度了。所以在32位计算机中,无论指针p指向何种类型的数据对象,其sizeof结果肯定为4bytes。

[cpp] view plaincopyprint?
  1. char *pc="abcd";  
  2. int *pi;  
  3. string *ps;  
  4. char **ppc=&pc;  
  5. void (*ppf)();//函数指针  
  6. cout<<"sizeof(pc)="<<sizeof(pc)<<endl;  
  7. cout<<"sizeof(pi)="<<sizeof(pi)<<endl;  
  8. cout<<"sizeof(ps)="<<sizeof(ps)<<endl;  
  9. cout<<"sizeof(ppc)="<<sizeof(ppc)<<endl;  
  10. cout<<"sizeof(ppf)="<<sizeof(ppf)<<endl;//这5个结果均为4bytes  

(3)数组类型的sizeof

数组的sizeof值等于数组所占用的内存字节数。

【切记】:有人以为sizeof(数组名)是求数组中元素的个数,这种认识是严重错误的。
例如:

[cpp] view plaincopyprint?
  1. char a1[]="abcd";  
  2. int a2[3];  
  3. sizeof(a1)=5*1=5bytes;//一个char类型占1byte  
  4. sizeof(a2)=3*4=12bytes;  

【注】:

(1)求数组元素的两种方法

[cpp] view plaincopyprint?
  1. int counts_a1=sizeof(a1)/sizeof(char);  
  2. int counts_a1=sizeof(a1)/sizeof(a1[0]);  

(2)当数组名被用作函数参数传递时,此时的数组名变蜕化成一个相应类型的指针了

[cpp] view plaincopyprint?
  1. void foo3(char a3[3])   
  2. {     
  3. int c3 = sizeof( a3 ); // c3 =3  这里c3=sizeof(a3),想当于c3=sizeof(char*),故c3=4!!!   
  4. }  
  5.   
  6. void foo4(char a4[])  
  7. {     
  8. int c4 = sizeof( a4 ); // 同上,c4 =4!!!    
  9. }  

(4)结构体的sizeof

结构体在内存中时如何实际存储的呢?

结构体的大小不是所有成员大小的简单的相加,还需要考虑一个很重要的问题---------系统在存储结构体变量时存在一个“边界对齐”的问题。其实,这是VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。编译器会按照成员列表的顺序一个接一个地给每个成员分配内存的,字节对齐的细节遵循以下3个原则:

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

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

下面用例子来说明VC是怎样存放结构体的。例如:

Eg1:

[cpp] view plaincopyprint?
  1. //结构体1   
  2. struct Mystruct1  
  3. {  
  4. double data_db;  
  5. char data_ch;  
  6. int data_i;  
  7. };  

分析:

为上面的结构体分配内存的时候,VC根据成员变量出现的顺序和对齐方式,先为第一个成员data_db分配内存,其起始地址跟结构的起始地址相同(即data_db的偏移量为0,刚好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;接下来为第二个成员data_ch分配内存,这时下一个可以分配的地址相对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把data_ch存放在偏移量为8的地方满足对齐方式,该成员变量占用sizeof(char)=1个字节;接下来为第三个成员type分配空间,这时下一个可以分配的地址相对于结构的起始地址的偏移量为9,不是sizeof(int)=4的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的地址相对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(MyStruct)=8+1+3+4=16,其中有3个字节是VC自动填充的,没有放任何有意义的东西。

Eg2:

[cpp] view plaincopyprint?
  1. //结构体2   
  2. struct Mystruct2  
  3. {  
  4. char data_ch;  
  5. double data_db;  
  6. int data_i;  
  7. };  

分析:

为上面的结构体分配内存的时候,VC根据成员变量出现的顺序和对齐方式,先为第一个成员data_ch分配内存,其起始地址跟结构的起始地址相同(即data_ch的偏移量为0,刚好为sizeof(char)的倍数),该成员变量占用sizeof(char)=1个字节;接下来为第二个成员data_db分配内存,这时下一个可以分配的地址相对于结构的起始地址的偏移量为1,不是sizeof(double)=8的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充7个字节(这7个字节没有放什么东西),这是下一个可以分配的地址相对于结构的起始地址的偏移量变为8,是sizeof(double)=8的倍数了,所以把data_db存放在偏移量为8的地方满足对齐方式,该成员变量占用sizeof(double)=8个字节。接下来为第三个成员data_i分配空间,这时下一个可以分配的地址相对于结构的起始地址的偏移量为16,是sizeof(int)=4的倍数,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:1+7+8+4=20,不是结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以还得在第三个成员变量data_i的后面填充4个字节,使其达到24个字节,成为字节边界数的倍数。所以整个结构的大小为:sizeof(MyStruct)=1+7+8+4+4=24,其中有7+4个字节是VC自动填充的,没有放任何有意义的东西。

Eg3:

[cpp] view plaincopyprint?
  1. #include <iostream>   
  2. using namespace std;  
  3.   
  4. char foo()  
  5. {  
  6.     cout<<"foo函数被调用!"<<endl;  
  7.     return 'a';  
  8. }  
  9.   
  10. int main()  
  11. {  
  12.     //内置数据类型   
  13.     int a=5;  
  14.     char b='c';  
  15.     cout<<"内置数据类型:"<<endl;  
  16.     cout<<sizeof(int)<<endl;  
  17.     cout<<sizeof(a)<<endl;  
  18.     cout<<sizeof(char)<<endl;  
  19.     cout<<sizeof(b)<<endl;  
  20.   
  21.     //对指针求其大小   
  22.     cout<<"指针数据类型:"<<endl;  
  23.     char *pc="abcd";  
  24.     int *pi;  
  25.     string *ps;  
  26.     char **ppc=&pc;  
  27.     void (*ppf)();//函数指针  
  28.     cout<<"sizeof(pc)="<<sizeof(pc)<<endl;  
  29.     cout<<"sizeof(pi)="<<sizeof(pi)<<endl;  
  30.     cout<<"sizeof(ps)="<<sizeof(ps)<<endl;  
  31.     cout<<"sizeof(ppc)="<<sizeof(ppc)<<endl;  
  32.     cout<<"sizeof(ppf)="<<sizeof(ppf)<<endl;  
  33.   
  34.   
  35.     //对函数调用求值   
  36.     cout<<"函数调用:"<<endl;  
  37.     int foo_size;  
  38.     foo_size=sizeof(foo());  
  39.     cout<<"sizeof(foo())="<<foo_size<<endl;//函数foo()并不会被调用,sizeof对一个函数调用求值,就相当于对该函数的返回类型求sizeof, 即sizeof(char)  
  40.   
  41.   
  42.     //对结构体求值   
  43.     cout<<"对结构体类型:"<<endl;  
  44.     struct ALIGN1  
  45.     {  
  46.         char a;  
  47.         int b;  
  48.         char c;  
  49.     };  
  50.       
  51.   
  52.     //调整ALIGN1中成员列表   
  53.     struct ALIGN3  
  54.     {  
  55.         int b;  
  56.         char a;  
  57.         char c;  
  58.     };  
  59.   
  60.     struct ALIGN2  
  61.     {  
  62.         double a;  
  63.         char b;  
  64.         int c;  
  65.     };  
  66.   
  67.     //再举一个例子   
  68.     //结构体1   
  69.     struct Mystruct1  
  70.     {  
  71.         double data_db;  
  72.         char data_ch;  
  73.         int data_i;  
  74.   
  75.     };  
  76.     //结构体2   
  77.     struct Mystruct2  
  78.     {  
  79.         char data_ch;  
  80.         double data_db;  
  81.         int data_i;  
  82.     };  
  83.   
  84.   
  85.   
  86.     cout<<"struct1的大小:"<<endl;  
  87.     cout<<sizeof(ALIGN1)<<endl;  
  88.     cout<<"a的偏移量:"<<offsetof(ALIGN1,a)<<endl;  
  89.     cout<<"b的偏移量:"<<offsetof(ALIGN1,b)<<endl;  
  90.     cout<<"c的偏移量:"<<offsetof(ALIGN1,c)<<endl;  
  91.   
  92.     cout<<"struct2的大小:"<<endl;  
  93.     cout<<sizeof(ALIGN2)<<endl;  
  94.   
  95.   
  96.     cout<<"struct3的大小:"<<endl;  
  97.     cout<<sizeof(ALIGN3)<<endl;  
  98.     cout<<"a的偏移量:"<<offsetof(ALIGN3,a)<<endl;  
  99.     cout<<"b的偏移量:"<<offsetof(ALIGN3,b)<<endl;  
  100.     cout<<"c的偏移量:"<<offsetof(ALIGN3,c)<<endl;  
  101.   
  102.     //输出Mystruct1和Mystruct2的大小   
  103.     cout<<"sizeof(Mystruct1)="<<sizeof(Mystruct1)<<endl;  
  104.     cout<<"sizeof(Mystruct2)="<<sizeof(Mystruct2)<<endl;  
  105.   
  106.     //查看Mystruct1和Mystruct2中变量的(相对于结构体的起始地址)偏移量  
  107.     cout<<"Mystruct1_data_db的偏移量="<<offsetof(Mystruct1,data_db)<<endl;  
  108.     cout<<"Mystruct1_data_ch的偏移量="<<offsetof(Mystruct1,data_ch)<<endl;  
  109.     cout<<"Mystruct1_data_i的偏移量="<<offsetof(Mystruct1,data_i)<<endl;  
  110.   
  111.     cout<<"Mystruct2_data_db的偏移量="<<offsetof(Mystruct2,data_db)<<endl;  
  112.     cout<<"Mystruct2_data_ch的偏移量="<<offsetof(Mystruct2,data_ch)<<endl;  
  113.     cout<<"Mystruct2_data_i的偏移量="<<offsetof(Mystruct2,data_i)<<endl;  
  114.   
  115.   
  116.   
  117.     return 0;  
  118. }  


 

以上都是一些简单的东西,但是要是时间久了,这些细枝末节就很可能被大家遗忘了,而且这些东西也是笔试中经常光顾的问题。小弟暂且将小文作为乍到CSDN的第一篇文章,花了2个多小时整理出来,愿与君共勉。

0 0
原创粉丝点击