sizeof()那点儿事儿

测试代码都是在VC++ 6.0和32位操作系统下做的测试,参考《C++国际标准1998》和《inside the c++ object model》。
   一,由sizeof()函数引发的故事:
   用sizeof()来计算一个空类的大小,代码如下:

[cpp]  view plain copy
  1. class ClassSize  
  2. {  
  3.   public:  
  4.   protected:  
  5.   private:  
  6. };  
  7. int main()  
  8. {  
  9.   int x = sizeof(ClassSize);  
  10.   cout<<"The result :"<<x<<endl;  
  11.   return 0;  
  12. }  

这个计算结果是什么?这个问题留给大家去试验.


 1,基本数据类型的sizeof

[cpp]  view plain copy
  1. cout<<sizeof(char)<<endl;     //              结果是1  
  2. cout<<sizeof(int)<<endl;                 //   结果是4  
  3. cout<<sizeof(unsigned int)<<endl;        //   结果是4  
  4. cout<<sizeof(long int)<<endl;              // 结果是4  
  5. cout<<sizeof(short int)<<endl;             // 结果是2  
  6. cout<<sizeof(float)<<endl;                //  结果是4  
  7. cout<<sizeof(double)<<endl;                // 结果是8  

这个估计是个程序员都能回答出来,但这里列举出来时.


 2,指针变量的sizeof

[cpp]  view plain copy
  1. char *pc ="abc";  
  2. sizeof( pc ); // 结果为4  
  3. sizeof(*pc);     // 结果为1  
  4. int *pi;   
  5. sizeof( pi ); //结果为4   
  6. sizeof(*pi);     //结果为4  
  7. char **ppc = &pc;   
  8. sizeof( ppc ); // 结果为4   
  9. sizeof( *ppc ); // 结果为4   
  10. sizeof( **ppc ); // 结果为1  
  11. void (*pf)();// 函数指针  
  12. sizeof( pf );// 结果为4  

sizeof( pc ),sizeof( pi ),sizeof( ppc ),sizeof( pf ),指针的大小就是4个字节;sizeof(*pc),指针指向的内容即字符串的首地址,当然是1个字节了;sizeof(*pi),整形4个字节;sizeof( *ppc ),sizeof( **ppc )就有点文字游戏的意味了.


3,数组的sizeof
数组的sizeof值等于数组所占用的内存字节数,如:

[cpp]  view plain copy
  1. char str[] = "abc";  
  2. int a2[3];  
  3. sizeof( a1 ); // 结果为4,字符 末尾还存在一个NULL终止符  
  4. sizeof( a2 ); // 结果为3*4=12(依赖于int)  


写到这里,提一问,下面的c3,c4值应该是多少呢

[cpp]  view plain copy
  1. void func(char a3[3])  
  2.  {  
  3.  int c3 = sizeof( a3 ); // c3 == 3?4  
  4.  }  
  5.  void func2(char a4[])  
  6.  {  
  7.  int c4 = sizeof( a4 ); // c4 == 4  
  8.  }  

也许当你试图回答c4的值时已经意识到c3答错了,是的,c3!=3。这里函数参数a3已不再是数组类型,而是蜕变成指针,相当于char* a3,为什么仔细想想就不难明白,我们调用函数func时,程序会在栈上分配一个大小为3的数组吗?不会!数组是“传址”的,调用者只需将实参的地址传递过去,所以a3自然为指针类型(char*),c3的值也就为4。

4,结构体的sizeof

[cpp]  view plain copy
  1. struct MyStruct  
  2. {  
  3.            double dda;  
  4.            char strdda;  
  5.            int nType;  
  6. };//结果为16  

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

[cpp]  view plain copy
  1. struct MyStruct  
  2. {  
  3.             double dda;  
  4.            char strdda;  
  5.           int type;  
  6.          char add;  
  7. };  

 5,含位域结构体的sizeof

[cpp]  view plain copy
  1. struct data  
  2. {  
  3. char f1 : 3;  
  4. char f2 : 4;  
  5. char f3 : 5;  
  6. };  

其内存布局为:
|_f1__|___f2__|_|____f3___|_____|
|_|_ |_|_|_ |_|_|_|_|_|_| _| _|_|_ |_|
0       3            7  8              13       16

位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(data)的结果为2。

6,含有联合体的结构体的sizeof

[cpp]  view plain copy
  1. struct stru1  
  2.  {  
  3.            char *ptr,ch;                   //有指针变成4+4  
  4.            union A            //后面跟了A定义了一个类型,不占内存,而后面不跟A,是声明了结构体的一个成员,占内存,  
  5.           {  
  6.               short a,b;  
  7.               unsigned int c:2, d:1;  
  8.            };  
  9.            struct stru1* next;                //指针占4  
  10.  };//这样是8+4=12个字节  
  11.  struct stru1  
  12.  {  
  13.            char *ptr,ch;                            
  14.            union              //联合体是结构体的成员,占内存,并且最大类型是unsigned int,占4  
  15.           {  
  16.                short a,b;  
  17.                unsigned int c:2, d:1;  
  18.            };  
  19.            struct stru1* next;                          
  20.  };//这样是8+4+4=16个字节  

看着有点迷糊吧,不要着急,我们慢慢来分析.首先我们再温习一下什么是联合:联合表示几个变量公用一个内存位置, 在不同的时间保存不同的数据类型 和不同长度的变量;当一个联合被说明时, 编译程序自动地产生一个变量, 其长度为联合中最大的变量长度。这下或许你有点明白了,不过我们再来看看下面的例子,你就会深刻理解了:

[cpp]  view plain copy
  1. union abc  
  2. {   
  3.       int i;   
  4.       char mm;   
  5.   };    

用上面说明的联合定义一个名为lgc的联合变量, 可写成: union abc lgc,现在编译器就为lgc分配了4个字节的内存,因为最大为int.如果你觉得还不明白,那你去试试把第一个结构体改为:

[cpp]  view plain copy
  1. struct stru1  
  2. {  
  3.           char *ptr,ch;                     
  4.           union A              
  5.          {  
  6.              short a,b;  
  7.              unsigned int c:2, d:1;  
  8.           };  
  9.    union A x;  
  10.           struct stru1* next;                  
  11. };//这样是8+4+4=16个字节  

7,结构体体含有结构体的sizeof

[cpp]  view plain copy
  1. struct stru1  
  2.      {  
  3.       char c;  
  4.       int i;  
  5.      };  
  6.  struct stru2  
  7.     {  
  8.      char c1;  
  9.      stru1 s;  
  10.      char c2;  
  11.      };  
  12.  cout<<sizeof(stru2);      //stru2=16  

stru1的最宽简单成员的类型为int,stru2在考虑最宽简单类型成员时是将S1“打散”看的,所以S3的最宽简单类型也为int,这样,通过S3定义的变量,其存储空间首地址需要被4整除,整个sizeof(S3)的值也应该被4整除。c1的偏移量为0,s的偏移量呢这时s是一个整体,它作为结构体变量也满足前面三个准则,所以其大小为8,偏移量为4,c1与s之间便需要3个填充字节,而c2与s之间就不需要了,所以c2的偏移量为12,算上c2的大小为13,13是不能被4整除的,这样末尾还得补上3个填充字节。最后得到sizeof(stru2)的值为16

8,带有#pragma pack的sizeof
   它是用来调整结构体对齐方式的,不同编译器名称和用法略有不同,VC6中通过#pragma pack实现,也可以直接修改/Zp编译开关。#pragma pack的基本用法为:#pragma pack( n ),n为字节对齐数,其取值为1、2、4、8、16,默认是8,如果这个值比结构体成员的sizeof值小,那么该成员的偏移量应该以此值为准,即是说,结构体成员的偏移量应该取二者的最小值,再看示例:

[cpp]  view plain copy
  1. #pragma pack(push) // 将当前pack设置压栈保存  
  2.  #pragma pack(2)// 必须在结构体定义之前使用  
  3.  struct stru1  
  4.  {  
  5.  char c;  
  6.  int i;  
  7.  };  
  8.  struct stru2  
  9.  {  
  10.  char c1;  
  11.  stru1 s;  
  12.  char c2  
  13.  };  
  14. #pragma pack(pop) // 恢复先前的pack设置  

计算sizeof(S1)时,min(2, sizeof(i))的值为2,所以i的偏移量为2,加上sizeof(i)等于6,能够被2整除,所以整个S1的大小为6。同样,对于sizeof(S3),s的偏移量为2,c2的偏移量为8,加上sizeof(c2)等于9,不能被2整除,添加一个填充字节,所以sizeof(S3)等于10。


9,空结构体或者空类的sizeof

[cpp]  view plain copy
  1. struct S5 { };  
  2.  sizeof( S5 ); // 结果为1  
  3.  class c1{ };  
  4.  sizeof( c1); // 结果为1  

为什么?随便就推荐一本书吧:inside the c++ object model,读者自己去找答案吧,绝对有帮助.

10,类的sizeof
   最后回到我们开始的地方,类的sizeof值等于类中成员变量所占用的内存字节数。如:

[cpp]  view plain copy
  1. class A  
  2. {  
  3. public:  
  4.          int b;  
  5.          float c;  
  6.          char d;  
  7. };  
  8. int main(void)  
  9. {  
  10. A object;  
  11. cout << "sizeof(object) is " << sizeof(object) << endl;  
  12. return 0 ;  
  13. }  

输出结果为12(我的机器上sizeof(float)值为4,字节对其前面已经讲过)。不过需要注意的是,如果类中存在静态成员变量,结果又会是什么样子呢?

[cpp]  view plain copy
  1. class A  
  2. {  
  3. public:  
  4.          static int a;  
  5.          int b;  
  6.          float c;  
  7.          char d;  
  8. };  
  9. int main()  
  10. {  
  11. A object;  
  12. cout << "sizeof(object) is " << sizeof(object) << endl;  
  13. return 0 ;  
  14. }  

结果仍然是12.因为在程序编译期间,就已经为static变量在静态存储区域分配了内存空间,并且这块内存在程序的整个运行期间都存在。而每次声明了类A的一个对象的时候,为该对象在堆上,根据对象的大小分配内存。如果类A中包含成员函数,那么又会是怎样的情况呢?看下面的例子:

[cpp]  view plain copy
  1. class A  
  2.  {  
  3.  public:  
  4.           static int a;  
  5.           int b;  
  6.           float c;  
  7.           char d;  
  8.           int add(int x,int y)  
  9.           {  
  10.             return x+y;  
  11.           }  
  12.  };  
  13.  int main()  
  14.  {  
  15.  A object;  
  16.  cout << "sizeof(object) is " << sizeof(object) << endl;  
  17.  b = object.add(3,4);  
  18.  cout << "sizeof(object) is " << sizeof(object) << endl;  
  19.  return 0 ;  
  20.  }  

结果仍为12.因为只有非静态类成员变量在新生成一个object的时候才需要自己的副本。所以每个非静态成员变量在生成新object需要内存,而function是不需要的。


转自:http://blog.csdn.net/a45872055555/article/details/38852959

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值