您的位置:首页 > 编程语言 > C语言/C++

[C++基础]C++笔试题中非常常见的sizeof问题

2012-11-29 12:13 483 查看
sizeof 是一个操作符(operator),其作用返回一个对象或数据类型所占的内存字节数
strlen(char*)函数求的是字符串的实际长度,它求得方法是从开始到遇到第一个'\0',如果你只定义没有给它赋初值,这个结果是不定的,它会从aa首地址一直找下去,直到遇到'\0'停止。strlen返回的是有效字符串长度,不包含结束符‘\0’。函数strcpy则连‘\0’一起复制。 

基本类型sizeof实例

#include "stdafx.h"

#include <stdlib.h>
#include <iostream>
using namespace std;
void testStrlen()
{
char aa[10];
char bb[10]={'\0'};
char cc[10]="jun";
cout<<strlen(aa)<<endl; //结果是不定的
cout<<strlen(bb)<<endl; //结果为0
cout<<strlen(cc)<<endl; //结果为3
cout<<sizeof(cc)<<endl; //10 而sizeof()函数返回的是变量声明后所占的内存数,不是实际长度。
}
void* Fun1()
{
printf("void\n");
return NULL;
}
char Fun2()
{
char c='c';
printf("char c='c'");
return c;
}
int Fun3()
{
int i=2;
printf("int i=2");
return i;
}
struct strc0
{
};
struct strc00
{
int getA();
};
struct strc000
{
int* getA()
{
return NULL;
}
};
struct strc1
{
int a;
char b;
};
struct strc2
{
char b;
int a;
};
struct strc3
{
char b;
int a;
double c;
};
struct strc4
{
double c;
char b;
int a;
};
struct strc5
{
char b;
double c;
int a;
};
struct strc6
{
int a;
double c;
char b;
};
struct strc7
{
int a;
double c;
char b;
char d;
};
class classA0
{
};
class classA00
{
int GetA();
char GetB();
};
class classA000
{
int GetA()
{
return 1;
}
char GetB()
{
return 'c';
}
};
class classA1
{
int a;
double b;
};
class classA2
{
char b;
double c;
int a;
};
class classA3
{
char b;
double c;
int a;
char d;
};
int _tmain(int argc, _TCHAR* argv[])
{
testStrlen();
//基本数据类型
printf("%d\n",sizeof(char));// 1
printf("%d\n",sizeof(short));// 2
printf("%d\n",sizeof(int));// printf("%d\n",sizeof(2)); 4
printf("%d\n",sizeof(long));// 4
printf("%d\n",sizeof(float));// 4
printf("%d\n",sizeof(double));// printf("%d\n",sizeof(2.12)); 8
printf("%d\n",sizeof(string));// 32

//函数:看其返回值,与内部申请的变量无关,且不调用函数
printf("%d\n",sizeof(Fun1()));// 4  //没有调用函数
printf("%d\n",sizeof(Fun2()));// 1
printf("%d\n",sizeof(Fun3()));// 4

//指针:占用内存字节数都为4
printf("%d\n",sizeof(void*));//4
printf("%d\n",sizeof(int*));// 4

//数组:其值是(下标*数据类型),当数组为参数时,则沦为了指针
int a1[4];
char a2[]="abcd";//char a2[4]="abcd";//数组界限溢出
char a3[6]="abcd\0";//说明“\0”是一个字符(转义字符)
char a4[20]="abc";
printf("%d\n",sizeof(a1));// 4*4=16
printf("%d\n",sizeof(a2));// 5,字符末尾还存在一个NULL的结束符
printf("%d\n",sizeof(a3));//6
printf("%d\n",sizeof(a4));//20,说明给a4分配了20个字节的内存空间,不论是否填充满
printf("%d\n",sizeof(a4[10]));//1,a4[4]是一个char类型的数据

//结构体,涉及到字节对齐的问题,而字节对齐的细节与编译器实现有关。
//结构体的总大小为结构体最宽基本类型成员大小的整数倍
//结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍
//自己总结的:结构体的大小=最宽基本类型成员大小+其前后丈量的个数。其成员函数不占用内存
printf("%d\n",sizeof(strc0));//1,空的结构体占内存为1
printf("%d\n",sizeof(strc00));//1,在结构体内什么的函数不占内存
printf("%d\n",sizeof(strc000));//1,在结构体内定义的函数不占内存,不论其返回值是什么。
printf("%d\n",sizeof(strc1));//8,说明结构体中存在字符对齐的现象
printf("%d\n",sizeof(strc2));//8
printf("%d\n",sizeof(strc3));//不是24,而是16
printf("%d\n",sizeof(strc4));//不是24,而是16
printf("%d\n",sizeof(strc5));//不是16,而是24
printf("%d\n",sizeof(strc6));//不是16,而是24
printf("%d\n",sizeof(strc7));//24

//共同体Union
union U
{
char* a;
double b;
float c;
};
cout<<"sizeof(uniion U)="<<sizeof(union U)<<endl;//8 结构体在内存组织上是顺序式的,共同体则是重叠式,各成员共享一段内存,所以整个共同体的sizeof也就是每个成员sizeof的最大值。

//类:与结构体一样。类的大小=最宽基本类型成员大小+其前后丈量的个数。且成员函数不占用内存
printf("%d\n",sizeof(classA0));//1
printf("%d\n",sizeof(classA00));//1,说明申明的函数不占内存
printf("%d\n",sizeof(classA000));//1,说明定义的函数不占内存
printf("%d\n",sizeof(classA1));//16,说明类中也存在字符对齐的现象
printf("%d\n",sizeof(classA2));//24
printf("%d\n",sizeof(classA3));//24

system("pause");
return 0;
}
最后总结:类/结构体的大小=最宽成员类型+其前后丈量的个数。且普通成员函数(包括指针函数和静态函数,不包括虚函数)不占用内存,不论其是否有返回值。

类(虚函数,抽象函数,静态变量,静态函数,虚继承)sizeof 实例 

#include "stdafx.h"
#include <iostream>
using namespace std;
//编译器为每个有虚函数的类都建立一个虚函数表(其大小不计算在类中),并为这个类安插一个指向虚函数表的指针,即每个有虚函数的类其大小至少为一个指针的大小4
class A
{
public:
int a;
void Function();
};
class B
{
public:
int a;
virtual void Function();
};
class C : public B
{
public:
char b;
};
class D : public B
{
public:
virtual void Function2();
};
class E
{
public:
static void Function();
};
class staticE
{
static int intVar;
static void fun(){}
};
void test1()
{
cout<<"sizeof(A)="<<sizeof(A)<<endl;//4 (内含一个int,普通函数不占大小)
cout<<"sizeof(B)="<<sizeof(B)<<endl;//8 (一个int ,一个虚函数表指针)
cout<<"sizeof(C)="<<sizeof(C)<<endl;//12 (一个int ,一个虚函数表指针,一个char ,再加上数据对齐)
cout<<"sizeof(D)="<<sizeof(D)<<endl;//8 (一个int ,一个虚函数表指针,多个虚函数是放在一个表里的,所以虚函数表指针只要一个就行了)
cout<<"sizeof(E)="<<sizeof(E)<<endl;//1 (static 函数不占大小,空类大小为1)
cout<<"sizeof(staticE)="<<sizeof(staticE)<<endl;//1 静态数据成员不计入类内
}
class cA
{
public:
virtual void PrintA1(void){}
virtual void PrintA2(void){}
};
class cB
{
public:
virtual void PrintB(void){}
};
class cC
{
public:
virtual void PrintC(void){}
};
class cD : public cA, public cB, public cC
{
};
class cE : public cA, public cB, public cC
{
public:
virtual void PrintE(void){}
};
void test2()
{
cout<<"sizeof(cD)="<<sizeof(cD)<<endl;//12 如果一个类里面什么也不实现,只实现一个或多个虚函数的话,测它的sizeof会得到4,但如果一个类从多个类继承,并且它的多个基类有虚函数的话,它就会有多个虚函数表了,这个在COM也有体现.
cout<<"sizeof(cE)="<<sizeof(cE)<<endl;//12
}
class dA
{
int a;
virtual ~dA(){}
};
class dB:virtual public dA
{
virtual void myfunB(){}
};
class dC:virtual public dA
{
virtual void myfunC(){}
};
class dD:public dB,public dC
{
virtual void myfunD(){}
};
void test3()
{
cout<<"sizeof(dA)="<<sizeof(dA)<<endl;//8
cout<<"sizeof(dB)="<<sizeof(dB)<<endl;//12
cout<<"sizeof(dC)="<<sizeof(dC)<<endl;//12
cout<<"sizeof(dD)="<<sizeof(dD)<<endl;//16
//解释:A中int+虚表指针。B,C中由于是虚继承因此大小为A+指向虚基类的指针,B,C虽然加入了自己的虚函数,但是虚表指针是和基类共享的,因此不会有自己的虚表指针。D由于B,C都是虚继承,因此D只包含一个A的副本,于是D大小就等于A+B中的指向虚基类的指针+C中的指向虚基类的指针。
//如果B,C不是虚继承,而是普通继承的话,那么A,B,C的大小都是8(没有指向虚基类的指针了),而D由于不是虚继承,因此包含两个A副本,大小为16. 注意此时虽然D的大小和虚继承一样,但是内存布局却不同。
}
class T1
{
};
class T2 : public T1
{
};
class T3 : public T1
{
int a;
};

class M1
{
virtual void GetM()=0;
};
class M2
{
virtual void GetM2()=0;
virtual void Get();
};
class M3 : public M1, public M2
{
int a;
};
void test4()
{
cout<<"sizeof(T2)="<<sizeof(T2)<<endl;//1 继承一个空父类,这时sizeof(父类)=0
cout<<"sizeof(T3)="<<sizeof(T3)<<endl;//4 同上
cout<<"sizeof(M1)="<<sizeof(M1)<<endl;// 4
cout<<"sizeof(M2)="<<sizeof(M2)<<endl;// 4
cout<<"sizeof(M3)="<<sizeof(M3)<<endl;// 12 抽象函数与虚函数一样
}

void main()
{
test1();
test2();
test3();
test4();
}
/*虚函数表(http://www.uml.org.cn/c%2B%2B/200811143.asp)
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。
对C++了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。 在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了 这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
debug时,在局部变量窗口中可以看到,虚函数表
*/备注:struct与class类似。
严重我这个发现:内存对齐(字节对齐,结构体对齐)
在类(结构体)中,空的类占内存1个字节,static修饰变量和函数都不占内存。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: