sizeof问题总结

来源:互联网 发布:淘宝百宝箱满减 编辑:程序博客网 时间:2024/06/05 20:48

sizeof的使用非常简单:求对象或者类型的大小。然后sizeof又非常复杂,它涉及到很多特殊情况。

不同类型占用字节情况:

      char :1个字节 
      指针: 4个字节
      short int : 2个字节 
      int:  4个字节 
      unsigned int : 4个字节 
      float:  4个字节 
      double:   8个字节 
      long:   4个字节 
      long long:  8个字节 
      unsigned long:  4个字节 


      64位编译器:

      char :1个字节 
      指针: 8个字节 
      short int : 2个字节 
      int:  4个字节 
      unsigned int : 4个字节 
      float:  4个字节 
      double:   8个字节 
      long:   8个字节 
      long long:  8个字节 
      unsigned long:  8个字节

以下文章以32位为准.

(1)      sizeof是操作符,不是函数。
MSDN:The sizeof keyword gives the amount of storage, in bytes, associate with a variable or a type(including aggregate types). This keyword returns a value of type size_t(typedef unsigned int size_t).

语法:sizeof(object);=> sizeof(1);   sizeof(typename);=>sizeof(int);  sizeof object;=>sizeof 1;


(2)      sizeof不能求得void类型的长度。
你不能用sizeof(void);这样将产生:error C2070: illegal sizeof operand
比如如下代码:
void fun(){
}
int main(){
  cout<<sizeof(fun())<<endl;
  return 0;
}//这个编译是不会通过的
char fun(){
         return ‘a’;
}
int main()
{
  cout<<sizeof(fun())<<endl;
  return 0;

}


(3)      sizeof能求得void类型的指针的长度。

目前基本上所有的编译器都把指针的大小看做是4byte。其实指针也是变量,只不过这个变量很特殊,它是存放其他变量的地址的变量。又由于目前32位计算机平台上的程序段的寻址范围是4GB,寻址的最小单元是Byte,4GB等于232byte。这么多得内存起地址如何编码,只需要用32个bit就行了,而32bit=32、8=4Byte,也就是说只要4Byte就能存储这些内存的地址了。因此对任何类型的指针变量进行sizeof运算其结果就是4.(指针类型用4Byte只存储了指针指向的地址,区分是何种类型的指针由编译器完成)


(4)      sizeof能求得静态分配的内存的数组的长度。

int a[10];sizeof(a); 这样可求得长度是10*4=40.(引申问题:数组名和指针的区别。参看下篇),另外注意char ch[]=”abc”; sizeof(ch);结果为4,注意字符串数组末尾有’\0’,通常我们可以利用sizeof来计算数组中包含的元素个数。其做法是:int n=sizeof(a)/sizeof(a[0]);


要注意对函数的形参数组使用sizeof的情况。如:
void fun(int arr[10]){
         int n = sizeof(arr);
}//n的值为4

这里的原因是在函数参数传递时,数组被转化成指针了。加入直接传递整个数组的话,那么必然涉及到数组元素的拷贝(实参到形参的拷贝),当数组非常大时,这会导致执行效率极低!而只传递数组的地址(即指针)那么只需要拷贝4Byte。


引申:sizeof和strlen的区别:
strlen(char*)函数求的是字符串的实际长度,它求的方法是从开始遇到第一个’\0’,如果你只定义没有给它赋初值,这个结果是不定的,它从指针指向的首地址一直找下去,直到遇到’\0’为止。
char aa[10];          ------------------                strlen(aa);      //结果是不定的   -----    sizeof(aa);      //结果是10
char aa[10]={'\0'};          ------------------          strlen(aa); //结果为0    -----     sizeof(aa);      //结果是10

char aa[]="LRP123";          ------------------          strlen(aa);  //结果为6    -----         sizeof(aa);      //结果是7

char aa[10] = "LRP123";     ---------------------       strlen(aa); //结果为6   -----         sizeof(aa);      //结果为10


char*aa=”0123456789”;   //字符串常量的情况
sizeof(aa);        //结果为4,指针的长度
sizeof(*aa);       //结果为1,第一个字符的空间
strlen(aa);         //结果为10,该字符串的长度
char aaa[]={"LRPLRP"};
int a,b;
fun(aaa,a,b);
void fun(char aaa[],int a,int b)
{
  a=strlen(aaa);
  b=sizeof(aaa);
}

//得到的a=6,b=4.在函数内部,虽然aaa转化成指针,但是strlen还是测试aaa开始直到’\0’为止的字符串的长度,而sizeof则是测指针的长度。


(5)      sizeof不能求得动态分配的内存的大小

例如int*a = new int[10]; int n = sizeof(a); 得到n的值是4。


(6)      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;

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


(7)       当表达式作为sizeof的操作数时,它返回表达式的计算类型大小,但是它不对表达式求值。(这点很重要)
char ch=1;
int num=1;
int n1=sizeof(ch+num);           //结果n1=4
int n2=sizeof(ch=ch+num);     //n2=1,ch=1
由于默认类型转换原因,ch+num的计算结果的类型是int,因此n1的值为4.而表达式ch=ch+num的结果的类型是char,记住虽然在计算ch+num时,结果为int,但是把结果赋值给ch时又进行了类型转换,因此表达式的最终类型还是char,因此n2等于1。

对于n2=sizeof(ch=ch+num); 乍一看改程序貌似实现了让ch加上num并赋值给ch的功能,事实并非如此,由于sizeof只关心类型大小,所以它自然不应该对表达式求值。但是,int len=3; cout<<sizeof(int[++len])<<”,”<<len;输出地答案却是16,4.(原文中作者说得时在Dev C++编译器下,但是经过在VC下测试,编译不通过。后来改成int num = 5;      int aaa[]={1,2,3,4,5,6};  cout<<sizeof(aaa[++num])<<","<<num<<endl;输出结果却是4,5.由此可见++num操作并未执行),因此尽量不要在sizeof中直接对表达式求大小,以免出现错误。


(8)      sizeof可以对函数调用求大小,并且求得的大小等于函数返回类型的大小,但是不执行函数体。
int fun(int &num)
{
  int a,b;
  a = 10;
  b = 2;
  num = a/b;
  return num;
}
int main()
{
  int num = 0;
  cout<<sizeof(fun(num))<<endl;
  cout<<num<<endl;
  return 0;
}//输出结果4 0
sizeof(fun(num))得到的是函数返回类型的大小,而fun(num)的返回值类型是int,所以等价于sizeof(int)得到结果为4.另外不要认为这个长度为fun函数内部的局部变量所占空间的大小(认为结果为8,即a和b两个int的长度也是错误的)另外也注意,sizeof(fun(num))也不是求函数指针的大小,切记是求得返回值类型的长度大小。
这里跟特征7很类似。sizeof的操作对象是函数调用时,它不执行函数体。为此,建议大家不要把函数体放在sizeof后面的括号里,这样让人误以为函数体执行了,其实它根本没执行。另外可知:不能对返回类型是void的函数使用sizeof求其大小,如2中讲得编译报错。

另外注意sizeof(fun)的大小是多少呢?其实得不到答案,原因是编译不通过。(很多人可能认为fun是函数名,而再把函数名等价于函数的地址,地址是指针),于是得到了其值是4.但是事实却不是这样,却是编译不通过。


(9)      sizeof求得结构体(及其对象)的大小并不等于各个数据成员对象的大小之和
结构体的大小跟结构体成员对其有密切关系,而并非简单地各个成员的大小之和。比如两个结构体AA和BB。,使用sizeof分别得出结果是24和16.可以看出sizeof(AA)并不等于sizeof(int)+sizeof(double)+sizeof(int)
struct AA{
       int aa;
       double bb;
       int cc;
};
struct BB{
       int aa;
       int bb;
       double cc;
};
结构体成员对齐的规则:(1)结构体大小等于结构体内最大成员大小的整数倍;(2)结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说double型成员相对于结构体的首地址的偏移量应该是8的整数倍;(3)为了满足规则1和规则2编译器会再结构体成员之后进行字节填充。

在进行设计时,最好仔细安排结构体中各个成员的顺序。


(10)  sizeof不能用于求结构体的位域成员的大小,但是可以求得包含位域成员的结构体的大小。
位域:类型的大小都是以字节(byte)为基本单位的,比如sizeof(char)为1byte。但是bool类型取值只有true和false,按理只用1bit就够了,但事实上sizeof(bool)等于1.为了提供一种能对变量的存储空间进行精打细算的机制,这就是位域。简单来说在结构体成员变量后面跟上的一个冒号+一个整数,就代表位域,请仔细看以下结构体:
struct A{
         bool b:1;
         char ch1:4;
         char ch2:4;
}item;
该结构体试图让bool类型的变量b只占用1个bit,让ch1和ch2分别只占用4个bit,以此来达到对内存精打细算的功能。语句sizeof(item.b)和sizeof(item.ch1)等对位域成员求大小的语句均不能通过编译。其原因:sizeof以byte为单位返回操作数的大小。
对包含位域的结构体可以使用sizeof求其大小,但其求值规则比较复杂,不涉及到成员对齐,还与集体编译环境有关。
延伸:class A{};sizeof(A)的结果为1,其原因:类的实例化是在类内存中分配一块地址,每个实例在内存中得都有独一无二的地址,同样空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以其长度为1。(编译器为了区分不同的类,在类中加了一个char类型,另外,如果一个文件中有256个空类,即大于char类型表示的范围,就不能区分空类了)
原创粉丝点击