深入理解计算机各种类型大小(sizeof)

1. 用法

1.1 sizeof和new、delete等一样,是关键字,不是函数或者宏。

1.2 sizeof返回内存中分配的字节数,它和操作系统的位数有关。例如在常见的32位系统中,int类型占4个字节;但是在16位系统中,int类型占2个字节。

1.3 sizeof的参数可以是类型,也可以是变量,还可以是常量。对于相同类型,以上3中形式参数的sizeof返回值相同。

[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>int a;  
  2. <span style="white-space:pre">    </span>sizeof(a); // = 4  
  3. <span style="white-space:pre">    </span>sizeof(int); // = 4  
  4. <span style="white-space:pre">    </span>sizeof(1); // = 4  

1.4 C99标准规定,函数、不能确定类型的表达式以及位域(bit-field)成员不能被计算s 
izeof值,即下面这些写法都是错误的。

[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>void fn() { }  
  2. <span style="white-space:pre">    </span>sizeof(fn); // error:函数  
  3. <span style="white-space:pre">    </span>sizeof(fn()); // error:不能确定类型  
  4. <span style="white-space:pre">    </span>struct S  
  5. <span style="white-space:pre">    </span>{  
  6.     <span style="white-space:pre">    </span>int a : 3;  
  7. <span style="white-space:pre">    </span>};   
  8. <span style="white-space:pre">    </span>S sa;    
  9. <span style="white-space:pre">    </span>sizeof( sa.a ); // error:位域成员<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>  

1.5 sizeof在编译阶段处理。由于sizeof不能被编译成机器码,所以sizeof的参数不能被编译,而是被替换成类型。

[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>int a = -1;  
  2. <span style="white-space:pre">    </span>sizeof(a=3); // = sizeof(a) = sizeof(int) = 4  
  3. <span style="white-space:pre">    </span>cout<<a<<endl; // 输出-1。由于“=”操作符返回左操作数的类型,赋值操作没有执行。  

2. 在32位系统中不同类型的内存分配

2.1 基本类型


[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>sizeof(int);        // = 4  
  2. <span style="white-space:pre">    </span>sizeof(double);     // = 8  
  3. <span style="white-space:pre">    </span>sizeof(char);       // = 1  
  4. <span style="white-space:pre">    </span>sizeof(bool);       // = 1  
  5. <span style="white-space:pre">    </span>sizeof(short);      // = 2  
  6. <span style="white-space:pre">    </span>sizeof(float);      // = 4  
  7. <span style="white-space:pre">    </span>sizeof(long);       // = 4<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>  

2.2 指针

指针在32位系统中占4个字节。

[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>sizeof(int *);         // = 4  
  2. <span style="white-space:pre">    </span>sizeof(double *);      // = 4  
  3. <span style="white-space:pre">    </span>sizeof(char *);        // = 4  

 

2.3 数组

2.3.1 数组的sizeof返回整个数组所占的字节数,即(数组元素个数×每个元素所占字节)。
[cpp]  view plain copy
  1. <span style="white-space:pre">        </span>int ai[] = {1, 2};  
  2. <span style="white-space:pre">        </span>sizeof(ai);          // = 2*4 = 8  
 
2.3.2 常量字符串与字符数组的内存分配方式相同。
[cpp]  view plain copy
  1. <span style="white-space:pre">        </span>char ac[] = "abcd";  //注意数组末尾的字符串终结符'\0'  
  2. <span style="white-space:pre">        </span>sizeof(ac);          // = 5*1 = 5  
  3. <span style="white-space:pre">        </span>sizeof("abcd");      // = 5*1 = 5<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>  
2.3.3 数组和指针所占的字节数不同,应注意区分。
[cpp]  view plain copy
  1. <span style="white-space:pre">        </span>int *pi = new int[10]; //这是指针  
  2. <span style="white-space:pre">        </span>sizeof(pi);            // = 4  
  3.   
  4. <span style="white-space:pre">        </span>int ai[10];  
  5. <span style="white-space:pre">        </span>int *p = ai;           //这还是指针  
  6. <span style="white-space:pre">        </span>sizeof(p);             // = 4  
  7.   
  8. <span style="white-space:pre">        </span>double* (*a)[3][6];    //看成(double *) (*a)[3][6],即一个3×6的二维数组,数组元素为指针,指向double类型。  
  9. <span style="white-space:pre">        </span>sizeof(a);             // = 4,a为指向上述二维数组的指针  
  10. <span style="white-space:pre">        </span>sizeof(*a);            // = sizeof(double *)*3*6 = 72,*a表示上述二维数组      
  11. <span style="white-space:pre">        </span>sizeof(**a);           // = sizeof(double *)*6 = 24,**a即*(*a),表示double*[6],是元素为double指针的一维数组。  
  12. <span style="white-space:pre">        </span>sizeof(***a);          // = sizeof(double *) = 4,表示上述一维数组中的第一个元素,元素类型为double指针。  
  13. <span style="white-space:pre">        </span>sizeof(****a);         // = sizeof(double) = 8,表示上述数组首元素指向的double类型。  
 2.3.4 函数形式参数中的数组会蜕变为指针,原因是数组参数“传址调用”,调用者只需将实参的地址传递过去。有一种情况例外,那就是参数是指向数组的指针。
[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>void acf(char p[3])     //参数类型是int[],表示指向int的指针  
  2. <span style="white-space:pre">    </span>{  
  3. <span style="white-space:pre">    </span>    sizeof( p );        // = 4  
  4. <span style="white-space:pre">    </span>}  
  5. <span style="white-space:pre">    </span>void aif(int p[])       //参数类型是int[],表示指向int的指针  
  6. <span style="white-space:pre">    </span>{  
  7. <span style="white-space:pre">    </span>    sizeof( p );        // = 4  
  8. <span style="white-space:pre">    </span>}  
  9. <span style="white-space:pre">    </span>void pif(int (*p)[6])   //参数类型是int (*)[6],表示指向int数组的指针  
  10. <span style="white-space:pre">    </span>{  
  11. <span style="white-space:pre">    </span>    sizeof( p);         // = 4  
  12. <span style="white-space:pre">    </span>    sizeof( *p );       // = sizeof(int)*6 = 24  
  13. <span style="white-space:pre">    </span>}  
  14. <span style="white-space:pre">    </span>void ppf(int *p[6])     //参数类型是int *[],表示指向int指针的指针  
  15. <span style="white-space:pre">    </span>{  
  16.     <span style="white-space:pre">    </span>    sizeof( p );        // = 4  
  17.    <span style="white-space:pre"> </span>    sizeof( *p );       // = 4  
  18. <span style="white-space:pre">    </span>}  

 

2.4. 类和结构体的内存分配。

2.4.1 空类或空结构体占一个字节。
[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>class CEmpty { };  
  2. <span style="white-space:pre">    </span>sizeof(CEmpty); // = 1  
  3.   
  4. <span style="white-space:pre">    </span>struct SEmpty { };  
  5. <span style="white-space:pre">    </span>sizeof(SEmpty); // = 1  
 
2.4.2 非空类和结构体所占字节为所有成员占字节的和,但是不包括成员函数和静态成员所占的空间。
[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>class CInt : public CEmpty {  
  2. <span style="white-space:pre">    </span>   int i;  
  3. <span style="white-space:pre">    </span>};  
  4. <span style="white-space:pre">    </span>sizeof(CInt); // = 4;  
  5.   
  6. <span style="white-space:pre">    </span>class CFunc {  
  7. <span style="white-space:pre">    </span>   void f() {}  
  8. <span style="white-space:pre">    </span>};  
  9. <span style="white-space:pre">    </span>sizeof(CFunc); // = 1;  
  10. <span style="white-space:pre">    </span>  
  11. <span style="white-space:pre">    </span>struct SInt : SEmpty {  
  12. <span style="white-space:pre">    </span>   static int i;  
  13. <span style="white-space:pre">    </span>};  
  14. <span style="white-space:pre">    </span>sizeof(SInt); // = 1;  

 

2.4.3 字节对齐
为了加快计算机的取数速度,编译器默认对内存进行字节对齐。对结构体(包括类)进行字节对齐的原则是:

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

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

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


[cpp]  view plain copy
  1. <span style="white-space:pre">        </span>struct SByte1  
  2. <span style="white-space:pre">        </span>{  
  3.     <span style="white-space:pre">        </span>double d;    // 偏移量0~7  
  4.     <span style="white-space:pre">        </span>char j;      // 偏移量8  
  5.     <span style="white-space:pre">        </span>int a;       // 偏移量12~15,由于9不能整除4,故先填充9~11  
  6. <span style="white-space:pre">        </span>};   
  7. <span style="white-space:pre">        </span>sizeof(SByte1);  // = 16  
  8.   
  9. <span style="white-space:pre">        </span>struct SByte2  
  10. <span style="white-space:pre">        </span>{        
  11.  <span style="white-space:pre">       </span>   char j;      // 偏移量0  
  12.     <span style="white-space:pre">    </span>   double d;    // 偏移量8~15,由于1不能整除8,故先填充1~7  
  13.     <span style="white-space:pre">    </span>   int a;       // 偏移量16~19  
  14. <span style="white-space:pre">        </span>};   
  15. <span style="white-space:pre">        </span>sizeof(SByte2);  // = 24,为了凑成8的倍数,填充20~23  
[cpp]  view plain copy
  1. <span style="white-space: pre;">  </span>另外,可以通过#pragma pack(n)来设定变量以n字节对齐方式。  
[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>#pragma pack(push) //保存对齐状态  
  2. <span style="white-space:pre">    </span>#pragma pack(4)    //设定为4字节对齐  
  3. <span style="white-space:pre">    </span>class CByte  
  4. <span style="white-space:pre">    </span>{  
  5. <span style="white-space:pre">    </span>   char c;        //偏移量0  
  6. <span style="white-space:pre">    </span>   double d;      //偏移量4~11,由于1不能整除4,故先填充1~3  
  7. <span style="white-space:pre">    </span>   int i;         //偏移量12~15  
  8. <span style="white-space:pre">    </span>};  
  9. <span style="white-space:pre">    </span>#pragma pack(pop)  //恢复对齐状态  
  10. <span style="white-space:pre">    </span>sizeof(CByte); // = 16<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>  
2.4.4 位域
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。 
2.4.4.1 位域以比特位作为单位,其长度不能大于一个字节。一个位域必须存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。
[cpp]  view plain copy
  1. <span style="white-space:pre">        </span>struct SBit1  
  2. <span style="white-space:pre">        </span>{  
  3.   
  4.   <span style="white-space:pre">      </span>   char a : 3;  
  5.    <span style="white-space:pre">     </span>   char b : 4;  
  6.    <span style="white-space:pre">     </span>   char c : 5;  
  7. <span style="white-space:pre">        </span>};  
  8. <span style="white-space:pre">        </span>sizeof(SBit1); // = (3+4+1+5+3) bits = 2 bytes  

2.4.4.2 使用空域可以有意使某位域从下一单元开始,但是空域不能使用。


  1. sizeof(SBit2); // = (3+4+1+5+3) bits = 3 bytes  
[cpp]  view plain copy
  1. <span style="white-space:pre">    </span>struct SBit2  
  2. <span style="white-space:pre">    </span>{  
  3. <span style="white-space:pre">    </span>    char a : 3;  
  4.  <span style="white-space:pre">   </span>   char   : 0;   // 空域  
  5.  <span style="white-space:pre">   </span>   char b : 4;  
  6.   <span style="white-space:pre">  </span>  char c : 5;  
  7. <span style="white-space:pre">    </span>};  
  8. <span style="white-space:pre">    </span>sizeof(SBit2); // = (3+4+1+5+3) bits = 3 bytes  
2.4.4.3 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式。  
[cpp]  view plain copy
  1. <span style="white-space:pre">        </span>struct SBit3  
  2. <span style="white-space:pre">        </span>{  
  3.  <span style="white-space:pre">       </span>   char  a : 3;  
  4.  <span style="white-space:pre">       </span>   short b : 4;  
  5.  <span style="white-space:pre">       </span>   char  c : 5;  
  6. <span style="white-space:pre">        </span>};  
  7. <span style="white-space:pre">        </span>sizeof(SBit3); // = 6 bytes,由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。  
2.4.4.4 如果位域字段之间穿插着非位域字段,则不进行压缩。
[cpp]  view plain copy
  1. <span style="white-space:pre">        </span>struct SBit4  
  2. <span style="white-space:pre">        </span>{  
  3.   <span style="white-space:pre">      </span>  int a : 3;  
  4.    <span style="white-space:pre">     </span> int b : 4;  
  5.    <span style="white-space:pre">     </span> int c;  
  6. <span style="white-space:pre">        </span>};  
  7. <span style="white-space:pre">        </span>sizeof(SBit4); // = 8 bytes  
2.4.4.5 整个结构体的总大小为最宽基本类型成员大小的整数倍
[cpp]  view plain copy
  1. {  
  2.     int a : 3;  
  3.     int b;  
  4.     int c : 5;  
  5. };  
  6. sizeof(SBit5); // = 12 bytes  

2.5 联合

联合表示若干数据成员取其一,故以叠加方式分配内存,所占字节数为最大数据成员所占的字节数。
Cpp代码  复制代码  收藏代码
  1. union U   
  2. {   
  3.     int i;   
  4.     char c;   
  5.     double d;   
  6. };   
  7. sizeof(U); // = Max(sizeof(i), sizeof(c), sizeof(d)) = sizeof(d) = 8  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值