sizeof是C的单目运算符,它给出操作数存储所需的字节数,其操作数可以是表达式或类型名。sizeof()是运算符,不是函数,没有函数调用开销。其结果是在编译阶段由编译器自动给出,相当于常数,不象函数那样在运行时才得到返回值。
sizeof常用来屏蔽同一类型变量在不同平台上占用不同空间的问题,能提高C程序的可移植性。不懂得利用sizeof就出现:int *p = malloc(800);
问:为什么直接分配800字节? 答:因为要分配200个整数的空间
问:为什么200整数就是800字节?答:因为在XX平台上整数是4字节,200个就是800字节
问:那其他平台上整数也是4字节么?如果不是怎么办?答:………
改为int *p=malloc(sizeof(int)*NUM);,所有平台表达形式就一致了,数据类型的平台差异就由编译器通过sizeof屏蔽(不同平台的编译器负责给出正确的sizeof(类型)值)。然而或许由于权限给了编译器,sizeof操作数覆盖面太宽,增加了很多语法花样。其实,知道在哪用sizeof更有实际意义,本来就是编译器自动处理,又何必人为掺和。下面内容适合应付C笔试,很多考官喜欢在这里作文章。
sizeof禁区
sizeof不能用于函数,不完全类型或位字段。不完全类型指存储空间未知的数据类型,如不定长数组、void型等。所以char char_v[]的sizeof(char_v)和sizeof(void)都不正确。
基本数据类型与指针
操作数为基本类型或一般变量时,sizeof得到类型或变量bytes,如int在16位系统占2bytes,32位系统4bytes。操作数是类型须加括弧,变量名可加可不加,如:
char c;
sizeof c;//变量名可不加(),同样得到变量类型所占bytes,这里等同于sizeof(char)
操作数为指针时sizeof得到指针型所占bytes,如32位系统4bytes。但指针牵涉到其所指内存变量的类型,易概念混淆。如:
int* a;//sizeof(a)为4,没异议,但不同人所指的4意义不一定相同哦
char *a; //sizeof(a)多少呢?答1就露馅了,char是谁的类型,sizeof(a)求的又是什么类型,正确答案仍是4。不明白就看看前文指针类型的含义。
数组
sizeof(数组)得到数组总bytes。结论简单,但前提要确定操作数是否为数组型。如char array[10],不同表达式中,array有时代表类型char [10]的数组;有时仅代表0号元素地址值:
sizeof(array)中,array代表数组型,sizeof得到数组总bytes,即10。
sizeof(*array)中,array是地址,*array是数组0号元素的值。所以sizeof(*array)得到数组单元的byte,即sizeof(char),为1。
sizeof(array+n)中(n=0,1,...),array还是地址,array+n也是地址,所以sizeof(array+n)得到指针大小,相当于sizeof(char *),为4。
此外数组作参数传进函数时,编译器传递数组首地址,所以函数内的sizeof(array)也取指针大小,如:
int func(char array[10]){ return sizeof(array); //数组参数退化为地址,sizeof(array)=4 }
struct/union
按理说sizeof求struct大小只需依次累加所有member所占bytes;sizeof求union则取最大member所占bytes。但如考虑内存对齐限制,情况复杂了。某些平台编译器会"对齐"存放,struct/union对齐可能包括:struct成员变量地址相对struct起始地址的偏移须为该变量自身占用bytes的倍数;struct/union总大小为最大成员所占bytes的倍数。举例:
struct MyStruct
{
double da;
char ca;
int ia;
};
编译器先为成员da分配空间,占用sizeof(double)=8字节,其起始地址和结构起始地址相同;然后为ca分配空间,这时可分配地址相对结构起始的offset为8,满足sizeof(char)=1倍数,所以ca放在offset 8bytes,自身占1byte;接着ia,此时offset为9,不是sizeof(int)=4的倍数,编译器自动填充3bytes对齐,offset变为12,可被4整除,ia存放在offset12处,占4bytes。至此所有member分配完,总size为8+1+3+4=16,刚好是结构的字节边界(结构中最大变量所占字节数sizeof(double)=8的倍数),不需再填充。所以最终结构大小为16,其中3bytes是编译器自动填充的空白字节。
交换一下成员变量的位置,变为:
struct MyStruct
{
char ca;
double da;
int ia;
};
这时分配过程为:ca offset为0,满足对齐,占1byte;da初始offset为1,不是sizeof(double)=8的倍数,填充7bytes对齐,保存在offset为8处,自身占8bytes;ia初始offset为16,是sizeof(int)=4倍数,不需填充,存放在offset为16处,自身占4bytes。至此所有member都分配了空间,总size为1+7+8+4=20。但20不能被da的sizeof(double)=8整除,不满足结构边界对齐,需额外填4bytes,(20+4)/8才满足。所以struct最终大小为:1+7+8+4+4=24。其中7+4=11是编译器填充的空白字节。
对于union型,sizeof以其最大成员的字节数为基础,边界对齐填充后得到结果。如不考虑对齐,sizeof a为11,如考虑对齐,为16。
union a{
int r;
double e;
char q[11];
};
以上struct和union内存对齐和sizeof结果,只为说明编译器的处理策略,不同编译器结果可能不同,只要知道有这么回事就行了。
另外:sizeof计算C++结构或类时,静态成员变量不影响结构或类的大小,因为静态变量的存储位置与结构或类的实例地址无关。
strlen与sizeof
计算字符串长度的strlen和sizeof在某些场合易混淆,这里做个对比:
1.sizeof是运算符,编译时由编译器给出结果;strlen是函数,运行中调用后得到结果。
2.sizeof可用类型做操作数,strlen只能用char*型变量做参数,且char*所指内存必须是以'\0'结尾的字符串,不能是字符数组。
3.sizeof得到某类型实际占用内存的大小,而strlen通过遍历到字符串结尾的'\0',得到字符串中的字符总数。如:
char str[20]="0123456789";
char* ss = "0123456789";
int a=strlen(str); //a=10,运行时计算得字符串长度
int b=sizeof(str); //b=20,编译期确定str数组实际占用内存的大小
int c=sizeof(ss) //ss是指向字符串的指针,sizeof取指针所占空间,4字节
int d=sizeof(*ss) //*ss是字符串第一个字符'0',所占的内存是char型,1字节
int e=strlen(ss) //e =10,获取字符串长度,一定要用 strlen