盘点SIZEOF操作符考点

来源:互联网 发布:python f.close 编辑:程序博客网 时间:2024/06/05 17:16

盘点SIZEOF操作符考点

最近,博主在准备找工作,刷了很多笔试题,一个常被公司考查的基础知识就是sizeof操作符。于是,博主下定决心详细盘点下sizeof操作符的考点,相信各位看了本篇博文再也不会对sizeof操作符有任何疑问。废话少说,文归正转。

基础概念:

Sizeof是C/C++中的一个操作符(operator),它以字节形式计算操作数的内存大小。操作数可为数据类型、对象、表达式、指针、数组。Sizeof返回值类型为size_t,在头文件stddef.h中定义,这是一个依赖于编译器的值,也就是说不同的编译器返回值会有差异。一般来说,有32位编译器和64位编译器之分。

sizeof有五种调用形式:
1) sizeof(type_name); // sizeof(数据类型);

2) sizeof(object); // sizeof(对象或表达式);

3) sizeof(pointer);//sizeof(指针);

4)sizeof(array);//sizeof(数组);

5) sizeof  object; // sizeof 对象(写法5可以用写法1代替,忘记写法5吧)

第一种形式:sizeof( type_name )返回数据类型占用内存的字节数。

下面列出常用数据类型在32位编译器和64编译器中的内存大小:

32位编译器字节数64位编译器字节数char1char1short int2short int2int4int4float4float4double8double8long4long8unsigned long4unsigned long8long long8long long8

这里稍微插入一个知识点,sizeof操作符应用在引用类型上的时候,返回的是包含引用对象所需的内存长度(即被引用对象的大小)。看两个实例

①sizeof(int&);由于引用的对象为int类型,相当于求int类型的内存长度,所以结果为sizeof(int&)为4;

sizeof(char&);由于引用的对象为char类型,相当于求char类型的内存长度,所以结果为sizeof(int&)为1;

第二种形式:sizeof(object)返回对象占用内存的字节数。

sizeof计算对象的大小实际是对对象数据类型的计算。所以,同种数据类型的不同对象其sizeof值都是一致的。对象可以进一步延伸成表达式,即sizeof可以对一个表达式求值,编译器根据表达式的结果数据类型来确定大小,一般不会对表达式进行计算。sizeof还可以对一个函数调用求值,其结果是函数返回值的数据类型内存大小,函数并不会被调用。为加深理解,举六个实例:

①    char a=’10’; sizeof(a);a数据类型为char,所以等价于 sizeof(char);

②    sizeof(22);// 22的数据类型为int,所以等价于sizeof(int);

③    sizeof(2); // 2的数据类型为int,所以等价于sizeof(int);

④    sizeof(22.2);// 22.2的数据类型为double,所以等价于 sizeof(double);

⑤    sizeof(22.2+22);//22.2的数据类型为double22的数据类型为int,表达式结果数据类型为double,所以等价于sizeof(double);

⑥    假设一个函数定义:

char test()
 {
    return'1';
 }

sizeof(test());//test()函数的返回值数据类型为char,所以等价于sizeof(char)

第三种形式:sizeof(pointer)返回指针占用内存的字节数。

指针是指向对象的内存地址,内存地址是当前编译器的位数。32位编译器的内存地址是32位的,即4个字节。64位编译器是64位的,即8个字节。所以,指针变量的sizeof值与指针所指的对象没有任何关系,只与当前编译器相关。在32位编译系统中,举六个实例:

① char* pc ="abc";//pc为C字符串指针(与C字符串数组不同,请看sizeof第四种形式的实例②),sizeof(pc)结果为4

② int* pi;//整型指针,sizeof(pi)结果为4

③ string* ps;//string类指针,sizeof(ps);结果为4

④ char** ppc =&pc;//指向字符类型指针的指针,sizeof(ppc)结果为4

⑤ void(*pf)();//函数指针sizeof(pf)结果为4

⑥ int (*p)[3];//数组指针sizeof(p)的结果为4

关于函数指针和数组指针的概念可以自行查资料,博主也会找时间发帖介绍。

第四种形式:sizeof(array)返回数组占用内存的字节数。

直接看六个实例:

①  char a[10];//字符数组。char数据类型占用内存的字节数为1,数组a中存储有10个字符,所以,sizeof(a)的结果为1*10=10;

②  char b[]=”abc”;// C字符串数组。char数据类型占用内存的字节数为1,C字符串数组末尾以’\0’字符作为结束符。所以,数组b中存储有4个字符,sizeof(b)的结果为1*4=4;

③  int c[5];// 整型数组。int数据类型占用内存的字节数为4,数组c中存储有5个整型数    据。所以,sizeof(c)的结果为4*5=20;

④  char*d[5];//字符指针数组。字符指针在32位编译器中占用内存的字节数为4,数组d存储有5个字符指针,所以,sizeof(d)的结果为4*5=20;

特例⑤假设一个函数定义:

void test1(char e[10])
{
  cout<<sizeof(e);
}

根据前四个实例分析:e为字符数组,test1函数中sizeof(e)结果应为1*10=10。其实,这是错误的结果!此例特殊之处就在于:当数组作为函数的形参时会自动退化为指针,所以,在32位编译系统中,test1函数中sizeof(e)结果应为4。

特例⑥:有二维数组如下定义:

int A[2][3]={1,2,3,4,5,6};

根据前面的讲解可知sizeof(A)的结果应该为4*6=24;那么sizeof(A+1)的结果是多少呢?先补充点二维数组的知识,数组名A默认指向第一行数组的地址(相当于是数组指针),A+1是指向第二行数组的地址。由于A为二维数组,这里的指针运算单位就是一行数据,而不是一个数据元素。既然A+1是一个内存地址,也就是指针,那么问题转换成求指针的sizeof值了。所以,在32位编译器中sizeof(A+1)的值为4。这个例子是想说明一点:当对数组进行加减等指针运算时会退化为指针


如果你已经掌握了上面的知识,一般的考试可以应付了。但是,要精益求精的话就请继续阅读。

下面补充两种sizeof的特殊情况:

1)  当数据类型或对象为结构体;

2)  当数据类型或对象为类;

直接看实例:

①   假设一个类定义如下:

class test
{
   //故意留空
};

test a;sizeof(a);//test为空类,没有任何成员变量或函数,即没有存储任何内容。但是由于空类仍然可以实例化(类的实例概念就是上文中提到的对象)。一个类能够实例化,编译器就需给它分配内存空间,来指示类实例的地址。编译器默认分配了一个字节,以便标记可能初始化的类实例,同时使空类占用的空间也最少。所以,sizeof(a)结果为1。

②  sizeof(test);//test为自定义的数据类型,且是空类。编译器默认分配了一个字节的内存空间,所以,sizeof(a)结果为1。

③  假设有一个结构体定义如下:

struct test1
{
  //故意留空
};

test1 b;sizeof(b);//test1为空结构体,没有任何成员变量或函数,即没有存储任何内容;但是由于空结构体仍然可以实例化。情况与空类相似,所以sizeof(b)结果也为1。

④  sizeof(test1);// test1为自定义的数据类型,且是空结构体。编译器默认分配了一个字节的内存空间,所以,sizeof(a)结果为1。

⑤  假设一个类定义如下:

class test2
{
  public:
      test2() {//故意留空}
     ~test2(){//故意留空}
      void swap(int &a,int &b)
      {
         int temp=a;
         a=b;
         b=temp;
      }
};

sizeof(test2);//test2为自定义的类,相比于test增加了构造函数、析构函数和swap函数。因为调用test2中的构造函数、析构函数或swap函数时只需知道类test2的内存地址,与test2的实例无关,所以,在test2的实例中不需额外增加内存空间。即sizeof(test2)结果仍为1。

⑥  假设一个类定义如下:

class test3
{
   virtual void func(){//故意留空}
};

sizeof(test3);//test3为非空类,且只有一个虚函数(虚析构函数、纯虚函数情况也一样)。C++的编译器一旦发现一个数据类型中有虚函数,就会为该数据类型生成虚函数表,并在该数据类型实例中添加一个指向虚函数表的指针。这就相当于求指针的内存空间,根据前面的讲述:在32位的编译系统上,一个指针占4个字节的空间,因此sizeof(test3)是4,在64位编译系统中,sizeof(test3)是8。

⑦  当自定义的类或结构体中含有成员变量时,情况又有不同,涉及到内存对齐的概念。直接看实例吧。

 假设有一个结构体定义如下:

struct test4//结构体(考查内存对齐)
{
 char b;
 int a[12];
 double y;
 int c[];//柔性数组,不占结构体内存
 static int n;//静态变量,不占结构体内存

 enum colour{red=1,bule};

 };

 sizeof(test4);//test4为非空类,且有一堆成员变量。当有成员变量时,要考虑变量的内存对齐,而且变量的声明顺序非常重要,影响内存对齐时要补齐的内存大小。test4中第一个声明的变量为char 类型,占一个字节的内存;第二个变量为整型的数组变量,数组大小为12,接下来根据int类型所占内存进行对齐,int占4个字节,所以在char后面要补3个字节才是4的倍数(补齐原则是:补齐之后内存总数是当前变量所占内存的整数倍数,且整数倍数是距离原内存最近的,也就说要补最少的内存使其满足内存总数是当前变量所占内存的整数倍数),接着计算整个数组所占内存大小为4*12=48字节,到现在所占的总内存为1+3+4*12=52字节;第三个变量为double类型,所占内存为8个字节,52不是8的倍数,所以要补4个字节,现在所占内存为52+4=56字节,再加上double类型本身占的内存8字节,总共为52+4+8=64字节;第四个变量比较特殊,声明了一个整型数组但是没有指定数组大小(也可以将其大小声明为0),这在结构体或类内是合法的声明,它有一个专有名字叫柔性数组,具体去查资料吧。我们只需要知道它不占结构体的内存即可,所以,现在结构体所占的内存还是64字节;第五个变量为静态整型变量,我们要知道静态变量是结构体所共用的变量,主要实现数据共享,它不属于任何一个结构体的实例,所以也不占结构体的内存;第六个变量是枚举类型,我们只需记住它不占结构体内存即可(当枚举类型不在结构体或类内定义时,变量的大小是 1<=sizeof(enum)<=sizeof(int) 字节 (1-4字节),枚举默认是用int类型来存储的,占4个字节)。现在结构体所占的内存还是64字节。你以为把所有变量对齐就结束了吗?内存对齐还没结束。现在所占总内存为64字节,以上变量中占内存最多的是double类型的8字节,最后要检查64是不是8的整数倍,检查之后发现64正好为8的整数倍数,不用补内存,到此才真正完成了内存对齐。最终,sizeof(test4)的结果为64字节。


关于sizeof操作符的介绍就到这里,如果你已经掌握了以上所有列出的情况,sizeof相关的题目对你来说已经太简单。

为了说明C++的博大精深,下面再提一个知识点:如果让你计算一个共用体的sizeof,你是不是了解呢?

看一个实例:

union test5
{
char a;
double b
};

sizeof(test5);//test5为共用体。在共用体内存声明的变量,它们的内存起始地址是相同的,也就是说test5的char类型的变量a和double类型的变量b共用一个字节。计算共用体所占的内存大小相当于计算共用体内部所占内存最大的变量占用的内存。具体来说,test5中声明了两个变量,double类型变量比char类型变量所占内存要大,所以sizeof(test5)的结果为8。




原创粉丝点击