C++ sizeof不完全总结
来源:互联网 发布:虚拟网络传销中的三虚 编辑:程序博客网 时间:2024/04/30 13:03
本文是我在复习c++时,遇到各种计算sizeof操作的不完全总结,由于个人水平有限,不一定保证所说的完全正确,欢迎大家交流。
sizeof 是C/C++的一个操作符,它的作用是返回一个对象或类型所占的内存的字节数。
MSDN上的解释为:
The sizeof keyword gives the amount ofstorage, in bytes, associated with avariable or a type(including aggregatetypes). This keyword returns a value of type size_t.
其返回值类型为size_t,在头文件stddef.h中定义。这是一个依赖于编译系统的值,一般定义为
typedef
unsigned int
size_t;
sizeof有三种语法形式,如下:
sizeof(object);//sizeof(对象);
sizeof(type_name);//sizeof(类型);
sizeofobject;//sizeof对象;
1. 作用于基本类型时
当sizeof作用于基本类型时,基本类型变量所占的内存大小和基本类型所占的大小一样,如下面代码所示。
#include <iostream>using namespace std;int main(){ char a; bool b; short c; int d; float f; double db; cout<<"sizeof(a): "<<sizeof(a); cout<<" , sizeof(char) :"<<sizeof(char)<<endl; cout<<"sizeof(b): "<<sizeof(b); cout<<" , sizeof(bool) :"<<sizeof(bool)<<endl; cout<<"sizeof(c): "<<sizeof(c); cout<<" , sizeof(short) :"<<sizeof(short)<<endl; cout<<"sizeof(d): "<<sizeof(d); cout<<" , sizeof(int) :"<<sizeof(int)<<endl; cout<<"sizeof(f): "<<sizeof(f); cout<<" , sizeof(float) :"<<sizeof(float)<<endl; cout<<"sizeof(db): "<<sizeof(db); cout<<" , sizeof(double) :"<<sizeof(double)<<endl; }
运行结果如下:
2. 当用于数组,指针时
当sizeof用于数组时,数组所占的内存为数组中每个元素所占的内存 *数组中元素的数目。当数组作为函数形参时,又有所不同。
#include <iostream>using namespace std;int main(){ char arr[10]; int aa[10]; double db[10]; cout<<"sizeof(char arr[10]) : "<<sizeof(arr)<<endl; //10 cout<<"sizeof(int aa[10]) : "<<sizeof(aa)<<endl; //40 cout<<"sizeof(double db[10]): "<<sizeof(db)<<endl; //80}
运行结果如下:
当sizeof运用指针p时,与作用于数组有些不同。指针是用来存储变量的地址的,在不同位的系统中,地址的寻址空间不一样,导致表示一个地址所需的位数也不同。
在32位系统中,一个地址占32位,即4个字节,而64位系统中需要8个字节。所以在32位系统中,指针所占的内存空间为4个字节。以下实验是在32位系统下完成的。
#include <iostream>using namespace std;int main(){ char ca; char carr[6]; char *pc = &ca; char *pcc = carr; cout<<"sizeof(pc) : "<<sizeof(pc)<<endl; //4, not 1 cout<<"sizeof(pcc) : "<<sizeof(pcc)<<endl; //4, not 6 cout<<"sizeof(*pc) : "<<sizeof(*pc)<<endl; //1 cout<<"sizeof(*pcc) : "<<sizeof(*pcc)<<endl; //1 cout<<"sizeof(\"hello\") : "<<sizeof("hello")<<endl; //6, include '\0' int ia; int iarr[5]; int *pi = &ia; int *pii = iarr; cout<<"sizeof(pi) : "<<sizeof(pi)<<endl; //4 cout<<"sizeof(pii) : "<<sizeof(pii)<<endl; //4 cout<<"sizeof(*pi) : "<<sizeof(*pi)<<endl; //4 cout<<"sizeof(*pii) : "<<sizeof(*pii)<<endl; //4 double da; double drr[2]; double *pd = &da; double *pdd = drr; cout<<"sizeof(pd) : "<<sizeof(pd)<<endl; //4 cout<<"sizeof(pdd) : "<<sizeof(pdd)<<endl; //4 cout<<"sizeof(*pd) : "<<sizeof(*pd)<<endl; //8 cout<<"sizeof(*pdd) : "<<sizeof(*pdd)<<endl; //8}
运行结果如下:
当数组作为函数形参时,传递过去的并不是数组本身,而是数组的地址,这在计算sizeof时,发生了一些令人诧异的现象。
#include <iostream>using namespace std;int cal(char arr[]){ return sizeof(arr);}int main(){ char arr[10]; cout<<sizeof(arr)<<endl; //10 cout<<cal(arr)<<endl;//4, not 10}
运行结果如下:
从结果可以看出,在cal函数中,传递的参数实际上是数组的地址。
3. Sizeof用于复合结构
Sizeof用于string时
string并没有规定实施的标准,和编译器有关,它输出的是string对象或类所占的内存大小,不同编译器的结果不一样,有的返回4,16,32。
#include <iostream>#include <string>using namespace std;int main(){ string str; cout<<"sizeof(str) : "<<sizeof(str)<<endl; //32, @vs2010 string str2 = "hello world!"; cout<<"sizeof(str2) : "<<sizeof(str2)<<endl; //32 string str3 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; cout<<"sizeof(str3) : "<<sizeof(str3)<<endl; //32 cout<<"sizeof(str3.c_str()) : "<<sizeof(str3.c_str())<<endl; //4}
运行结果如下:
Sizeof用于结构体
Sizeof用于结构体时,返回值为结构体中每一个成员变量所占的内存之和,如果一个结构体为空,那么它所占的内存为1个字节。但需要注意的是,在结构体中,需要注意内存对齐的问题。
一、内存对齐的原因
大部分的参考资料都是如是说的:
1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
二、对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。
对齐步骤:
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3、结合1、2颗推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。
备注:数组成员按长度按数组类型长度计算,如char t[9],在第1步中数据自身长度按1算,累加结构体时长度为9;第2步中,找最大数据长度时,如果结构体T有复杂类型成员A的,该A成员的长度为该复杂类型成员A的最大成员长度。
当不使用#pragmapack时
#include <iostream>#include <string>using namespace std;struct A{};struct B{ char c; //1,char以1为对齐系数,所以 offset[0,3],包括填充字节 int a; //4,int以4为对齐系数,所以 offset[4,7] short b; //2,short以2为对齐系数,所以 offset[8,15] double d; //8, double 以8为对齐系数,所以offset[16,23] //结构整体以double的大小(8个字节)为对齐依据,所以b占8个字节 //其中2个是自身数据,后面6个字节是填充的 //整个size为 8+8+8 = 24 字节 //如果没有d,则整体按int的大小对齐,共占(4+4+4)=12字节};struct C{ char c; //1, char以1为对齐系数,所以offset[0,1] short b; //2, short以2为对齐系数,所以offset[2,3] int a; //4, int以4为对齐系数,所以offset[4,7] double d; //8, double以8无对齐系数,所以offset[8,15] //整体的size为 8(c,b,a)+8(d) = 16字节 //如果没有d,则整体按int的大小对齐,共占(2+2+4)=8字节 };struct D{ char arr[6]; //虽然是6个字符的数组,但以char大小对齐 char temp; //整个大小为 6+1 = 7 字节};int main(){ cout<<sizeof(A)<<endl; //1 cout<<sizeof(B)<<endl; //24 cout<<sizeof(C)<<endl; //16 cout<<sizeof(D)<<endl; //7}
运行结果如下:
从B与C的对比中,我们可以发现,同样的数据,对齐方式不同,所占的内存大小也不一样。这对我们以后自己写程序有很大启示。
现在改变一下,以#pragma pack指定对齐大小
当使用#pragma pack(1)时
#include <iostream>#include <string>using namespace std;#pragma pack(1)struct A{};struct B{ char c; int a; short b; double d; // 1+4+2+8 = 15字节};struct C{ char c; short b; int a; double d; // 1+2+4+8 = 15字节};struct D{ char arr[6]; char temp; //6+1= 7字节};int main(){ cout<<sizeof(A)<<endl; // 1 cout<<sizeof(B)<<endl; //15 cout<<sizeof(C)<<endl; //15 cout<<sizeof(D)<<endl; //7}
运行结果如下:
当使用#pragma pack(2)时
#include <iostream>#include <string>using namespace std;#pragma pack(2)struct A{};struct B{ char c; //offset[0,1] int a; //ofset[2,5] short b; //ofset[6,7] double d; //offset[8,15] // 2+4+2+8 = 16字节};struct C{ char c; //offset[0,1] short b; //offset[2,3] int a; //offset[4,7] double d; //offset[8,15] // 2+2+4+8 = 16字节};struct D{ char arr[6]; //offset[0,5] char temp; //offset[6,6] //6+1= 7字节 ,对齐系数为pragma参数和最大成员二者中的较小值};int main(){ cout<<sizeof(A)<<endl; // 1 cout<<sizeof(B)<<endl; //16 cout<<sizeof(C)<<endl; //16 cout<<sizeof(D)<<endl; //7}
运行结果如下:
当使用#pragma pack(4)时
#include <iostream>#include <string>using namespace std;#pragma pack(4)struct A{};struct B{ char c; //offset[0,3] int a; //ofset[4,7] short b; //ofset[8,11] double d; //offset[12,19] // 4+4+4+8 = 20字节};struct C{ char c; //offset[0,1] short b; //offset[2,3] int a; //offset[4,7] double d; //offset[8,15] // 2+2+4+8 = 16字节};struct D{ char arr[6]; //offset[0,5] char temp; //offset[6,6] //6+1= 7字节 ,对齐系数为pragma参数和最大成员二者中的较小值};int main(){ cout<<sizeof(A)<<endl; // 1 cout<<sizeof(B)<<endl; //20 cout<<sizeof(C)<<endl; //16 cout<<sizeof(D)<<endl; //7}
运行结果如下:
当结构体的成员中含有结构体时
当一个结构体A包含另一个结构体B时,计算A所占用的内存空间时,需要先将B中的每个成员展开来,然后再确定对齐系数,但在后面计算时,需要将B看成一个整体,具体例子见下面程序。
#include <iostream>#include <string>using namespace std;struct S1{ char a; //1 int b; //4 //size = 4+4 = 8字节};struct S2{ short sh; //offset[0,1] char cc; //offset[2,3] S1 s; //offset[4,11] //S2在考虑时对齐系数时,是把S1展开,然后再选择对齐系数 //所以对齐系数是 4 //但是在计算时 ,确需要将S1当成一个整体,所以 //size = 4+8 = 12字节};struct S3{ char a; short b; //size = 2+2 = 4字节};struct S4{ int aa; //offset[0,3] short bb; //offset[4,7] S3 s; //offset[8,15] double dd; //offset[16,23] //将S3展开考虑对齐系数,应为double比较大,所以对齐系数为8 //size = 4+4 + 8+ 8 = 24字节 };int main(){ cout<<"sizeof(S1): "<<sizeof(S1)<<endl; //8 cout<<"sizeof(S2): "<<sizeof(S2)<<endl; //12 cout<<"sizeof(S3): "<<sizeof(S3)<<endl; //4 cout<<"sizeof(S4): "<<sizeof(S4)<<endl; //24}
运行结果如下所示:
Sizeof用于union时
结构体在内存组织上是顺序式的,联合体则是重叠式,各成员共享一段内存,所以整个联合体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是复合类型,这里,复合类型成员是被作为整体考虑的。
#include <iostream>#include <string>using namespace std;struct S1{ char a; //1 short b; //2 //size = 2+2 = 4字节};struct S2{ char arr[6]; // size = 6字节};union U{ char c; //1字节 S1 s1; //4字节 S2 s2; //6字节 //size = 6字节};int main(){ cout<<"sizeof(S1): "<<sizeof(S1)<<endl; //4 cout<<"sizeof(S2): "<<sizeof(S2)<<endl; //6 cout<<"sizeof(S3): "<<sizeof(U)<<endl; //6}
运行结果如下:
4.Sizeof用于类时
当类和结构体只有除了访问方式之外,没有其他区别时,其计算内存方式和结构体类似,但当存在继承关系时,就有所不同了。
#include <iostream>#include <string>using namespace std;class A{private: int a; //offset[0,3] short b; //offset[4,5] char c; //offset[6,7]};class B: public A{ double a; int b; char c; //B占了24个字节,除了新的的16个字节之外,还有A的成员部分};class C{ char c; short b;};class D:public C{ double d; //d占16个字节,除了新增的8个字节外,还有C中的4个字节,填充到8个字节};int main(){ cout<<sizeof(A)<<endl; //8 cout<<sizeof(B)<<endl; //24 cout<<sizeof(C)<<endl; //4 cout<<sizeof(D)<<endl; //16}
运行结果如下:
当一个类中包含有static成员时,计算该类型对象所占用的内存空间时,不需要将static成员考虑进去,因为static成员是类共有的,并不是某一个对象所单独拥有的。
#include <iostream>#include <string>using namespace std;class A{private: short b; char c; static int count;};int main(){ A aa; cout<<sizeof(aa)<<endl; //4 cout<<sizeof(A)<<endl; //4}
运行结果如下:
当类中存在虚函数时
(C++ primer plus)编译器处理虚函数的方法是:给每个对象添加一个隐藏成员。隐藏成员中保存了一个函数地址数组的指针。这种数组成为虚函数表。
虚函数表中存储了为类对象进行声明的虚函数的地址。基类的对象包含一个指针,该指针指向基类中所有虚函数的地址表。
派生类对象将包含一个指向独立地址表的指针。如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址;
如果派生类没有重新定义虚函数,该虚函数表将保存函数原始版本的地址。如果派生类定义了新的虚函数,则该函数的地址也将被添加到虚函数表中。
注意,无论类中包含的虚函数时1个还是10个,都只需要在对象中添加一个地址成员,只是表的大小不同而已。
下面这张图是 c++ primer plus 中的虚函数表示意图。
实例如下:
#include <iostream>#include <string>using namespace std;class A{private: int a; short b;public : virtual void fun1(); virtual void fun2(); //基础数据占8个字节,虚函数表占4个字节 //一共12字节};class B:public A{ int d; virtual void fun1(); virtual void fun2(); //A中数据占8个字节,新增数据占4个字节 //虚函数表占4个字节,一共 16字节};int main(){ cout<<sizeof(A)<<endl; //12 cout<<sizeof(B)<<endl; //16}
运行结果如下:
后续如果有新的内容会继续补充。
- C++ sizeof不完全总结
- C/C++ sizeof 总结
- C/C++ sizeof总结
- C/C++ sizeof总结
- C/C++ sizeof 总结
- c 中sizeof 总结
- c/c++ sizeof总结
- C语言 sizeof总结
- 【C/C++】sizeof问题总结
- c语言详解sizeof & sizeof函数总结
- 不完全总结linux下c访问http
- C\C++ sizeof 陷阱&&总结
- 不完全总结
- 转(c/c++)sizeof()总结
- C语言中sizeof用法总结
- C\C++ sizeof 陷阱&&总结
- C/C++学习笔记9:sizeof总结
- C/C++中sizeof关键字总结
- HD5228 ZCC loves straight flush
- MAVEN 生命周期与插件笔记
- 英文
- 第一题
- 深入分析Java ClassLoader原理
- C++ sizeof不完全总结
- React入门指南
- python学习笔记
- Struct和Class的区别
- IEnumerable和IEnumerator 详解
- CocoaPods详解之----使用篇
- 第二题
- 配置apache虚拟目录与虚拟主机
- java常用的一些关键字