zoukankan      html  css  js  c++  java
  • c++ --> sizeof()使用小结

    sizeof()使用小结

     

    特性0:sizeof是运算符,不是函数

      sizeof最基本特性,后面的很多特性都是受到这个特性的影响,正因为sizeof不是函数,因此不把它所要求得长度的对象叫做参数,习惯上叫做操作数。

    特性1:sizeof不能求得void类型的长度

      不能用sizeof(void),这将导致编译错误:illegalsizeof operand。因为无法声明void类型的变量,可以试试void a; 编译器会报错:illegal use of type 'void'。声明变量的一个重要作用就是告诉编译器该变量需要多少存储空间。但void是“空类型”,你可以理解成不知道存储空间大小的类型。既然编译器无法确定void类型的变量的存储大小,那么它自然不让你声明这样的变量。当然了,声明void类型的指针是可以的!这就是特性2的内容。

    特性2:sizeof能求得void类型的指针的长度

      编译器可以确定void类型的指针所占用的存储空间,大小是4byte,如sizeof(int*);sizeof(void*);sizeof(double*);sizeof(Person*);等等。指针也是变量,只不过这个变量很特殊,它是存放其他变量的地址的变量。又由于目前32位计算机平台上的程序段的寻址范围都是4GB,寻址的最小单元是byte,4GB等于232Byte,这么多的内存其地址如果编码呢,只需要用32个bit就行了,而32bit = 32/8 = 4byte,也就是说只需要4byte就能存储这些内存的地址了。因此对任何类型的指针变量进行sizeof运算其结果就是4!

    特性3:sizeof能求得静态分配内存的数组的长度!

      int a[10]; int n = sizeof(a); 假设sizeof(int)等于4,则n= 10*4=40;特别要注意:char ch[]=”abc”;sizeof(ch);结果为4,注意字符串数组末尾有’’!通常我们可以利用sizeof来计算数组中包含的元素个数,其做法是:int n = sizeof(a)/sizeof(a[0]); 非常需要注意的是对函数的形参数组使用sizeof的情况。举例来说,假设有如下的函数:

    void fun(int array[10])
    {
         int n = sizeof(array);
    }

      这里n等于4,事实上不管形参是int的型数组,还是float型数组,或者其他任何用户自定义类型的数组,也不管数组包含多少个元素,这里的n都是4!原因是在函数参数传递时,数组被转化成指针了,原因:假如直接传递整个数组的话,那么必然涉及到数组元素的拷贝(实参到形参的拷贝),当数组非常大时,这会导致函数执行效率极低!而只传递数组的地址(即指针)那么只需要拷贝4byte。

    特性4:sizeof不能求得动态分配的内存的大小!

      假如有如下语句:int*a = new int[10]; int n = sizeof(a); 那么n的值是多少呢?是40吗?答案是否定的!其实n等于4,因为a是指针,在特性2中讲过:在32位平台下,所有指针的大小都是4byte!很多人都认为数组名就是指针,其实不然,二者有很多区别的,可参看《c专家编程》。

      有人认为sizeof是编译时进行求值的,并给出理由:语句 int array[sizeof(int)*10]; 能编译通过。但在编译器(如DEV C++)中定义动态数组,即:语句:int num; cin>>num;  int arrary[num];是对的(注意在vc6.0中是错的)。同样在DEV C++中对刚才的array利用语句int n=sizeof(array);cout<<n<<endl来求大小,结果编译通过,运行时输入num的值10之后,输出n等于40!在这里很明显num的值是运行时才输入的,因此sizeof不可能在编译时就求得array的大小!这样一来sizeof又变成是运行时求值的了。

      那么到底sizeof是编译时求值还是运行时求值呢?最开初c标准规定sizeof只能编译时求值,后来c99又补充规定sizeof可以运行时求值。但值得注意的是,即便是在实现了c99标准的DEV C++中仍然不能用sizeof求得动态分配的内存的大小!

    特性5:sizeof不能对不完整的数组求长度!

      在阐述该特性之前,我们假设有两个源文件:file1.cpp和file2.cpp,其中file1.cpp中有如下的定义:

    int arrayA[10] = {1,2,3,4,5,6,7,8,9,10};
    int arrayB[10] = {11,12,13,14,15,16,17,18,19,20};

      file2.cpp包含如下几个语句:

    extern arrayA[];
    extern arrayB[10];
    cout<<sizeof(arrayA)<<endl;       //编译出错!!
    cout<<sizeof(arrayB)<<endl;       //40

      原因是sizeof(arrayA)试图求不完整数组的大小。这里的不完整的数组是指数组大小没有确定的数组!sizeof运算符的功能就是求某种对象的大小,然而声明:extern int arrayA[]只是告诉编译器arrayA是一个整型数组,但是并没告诉编译器它包含多少个元素,因此对file2.cpp中的sizeof来说它无法求出arrayA的大小,所以编译器干脆不让你通过编译。

      那为什么sizeof(arrayB)又可以得到arraryB的大小呢?关键就在于在file2.cpp中其声明时使用externint arrayB[10]明确地告诉编译器arrayB是一个包含10个元素的整型数组,因此大小是确定的。

     

    特性6:当表达式作为sizeof的操作数时,它返回表达式的计算结果的类型大小,但是它不对表达式求值!

      为了说明这个问题,我们来看如下的程序语句:

    char ch = 1;
    int num = 1;
    int n1 = sizeof(ch + num);     //4
    int n2 = sizeof(ch = ch+num);  //1, 其中ch = 1

      执行上面的程序之后,n1等于4,n2等于1,ch等于1。由于默认类型转换的原因,表达式ch+num的计算结果的类型是int,因此n1的值为4!而表达式ch=ch+num;的结果的类型是char,虽然在计算ch+num时,结果为int,但是当把结果赋值给ch时又进行了类型转换,因此表达式的最终类型还是char,所以n2等于1。n1, n2的值分别为4和1,其原因正是因为sizeof返回的是表达式计算结果的类型大小,而不是表达式中占用最大内存的变量的类型大小!

     

    特性7:sizeof可以对函数调用求大小,并且求得的大小等于返回类型的大小,但是不执行函数体!

      假设有如下函数:

    int fun(int& num,const int& inc)
    {
         float div = 2.0;
         double ret =0;
         num = num+inc;
         ret = num/div;
         return ret;
    }
    
    int a = 3;
    int b = 5;
    cout << sizeof(fun(a,b)) << endl;  //4,操作对象是函数调用时,不执行函数体
    cout << a << endl;                 //3

      首先sizeof(fun(a,b))的值:其正确是4,因为用sizeof求函数调用的大小时,它得到的是函数返回类型的大小,而fun(a,b)的返回类型是int,sizeof(int)等于4。函数的返回类型和返回值的类型是不一样的。来看语句:cout<<sizeof(fun);其答案是多少呢?其实它得不到答案,原因是编译就通不过!原因待补充。

    特性8:sizeof求得的结构体(及其对象)的大小并不等于各个数据成员对象的大小之和!

      结构体的大小跟结构体成员对齐有密切关系,而并非简单地等于各个成员的大小之和!比如对如下结构体两个结构体A、B使用sizeof的结果分别是:16,24。可以看出sizeof(B)并不等于sizeof(int)+sizeof(double)+sizeof(int)=16。

    struct A{

             int num1;

             int num2;

             double num3;

    };

    struct B{

             int num1;

             double num3;

             int num2;

    };

    其规则如下:

      1.结构体的大小等于结构体内最大成员大小的整数倍

      2.结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,如double成员相对于结构体的首地址的地址偏移量应该是8的倍数。

      3.为了满足规则1和2编译器会在结构体成员之后进行字节填充!

      在进行设计时,要安排好结构体中各个成员的顺序,顺序略有差异,可能导致B比A多消耗了50%的空间!

    特性9:sizeof不能用于求结构体的位域成员的大小,但是可以求得包含位域成员的结构体的大小!

      首先解释一下什么是位域:类型的大小都是以字节(byte)为基本单位的,比如sizeof(char)为1byte,sizeof(int)为4byte等。但bool类型只取值true和false,按理所只用1bit(即1/8byte)就够了,但事实上sizeof(bool)等于1。因此可以认为bool变量浪费了87.5%(7/8)的存储空间!这在某些存储空间有限的设备(比如嵌入式设备)上是不合适的,为此需要提供一种能对变量的存储空间精打细算的机制,这就是位域。简单来说,在结构体的成员变量后面跟上的一个冒号 + 一个整数,就代表位域,请看如下的结构体:

    struct A
    {
         Bool b:1;
         char ch1:4;
         char ch2:4;
    }item; 

      该结构体试图让bool类型的变量b只占用1个bit,让ch1和ch2分别只占用4个bit,以此来达到对内存精打细算的功能c语言规定位域只能用于int,signed int或者unsigned int类型,C++又补充了char和long类型!你不能这样使用位域:floatf:8;这是不能通过编译的。并且位域变量不能在函数或者全局区定义,只能在结构体,自定义类,联合(union)中使用!

      基于上面的结构体,语句sizeof(item.b)和sizeof(item.ch1)等对位域成员求大小的语句均不能通过编译。其原因能再本篇的概论中找到:sizeof以byte为单位返回操作数的大小!那么sizeof(A)能否通过编译呢?对包含位域的结构体是可以使用sizeof求其大小的,但其求值规则比较复杂,不仅涉及到成员对齐,还与具体编译环境有关!在这里你只需要知道可以对包含位域的结构体使用sizeof求其大小。

    参考:http://blog.csdn.net/w57w57w57/article/details/6626840

    版权:w57w57w57

  • 相关阅读:
    一个tomcat设置多个端口,多个端口对应多个应用
    Docker 容器镜像删除
    Centos7上安装docker
    oracle锁表与解表
    windows共享文件夹
    tomcat部署项目后,war包是否可刪?war包存在必要性!
    解决Windows对JDK默认版本切换问题
    FLASHBACK介绍
    ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效
    Linux journalctl命令
  • 原文地址:https://www.cnblogs.com/jeakeven/p/5141563.html
Copyright © 2011-2022 走看看