C语言中判断数据类型或者表达式长度符;不是一个函数,字节数的计算在程序编译时进行,而不是在程序执行的过程中才计算出来。
用法:
sizeof(类型说明符,数组名或表达式);
或sizeof 变量名
sizeof是C/C++中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。
sizeof有三种语法形式,如下:
1 2 3 | sizeof(object);//sizeof(对象); sizeof(type_name);//sizeof(类型); sizeofobject;//sizeof对象; |
所以,
1 2 3 4 5 | inti; sizeof(i);//ok sizeofi;//ok sizeof(int);//ok sizeofint;//error |
既然写法3可以用写法1代替,为求形式统一以及减少我们大脑的负担,第3种写法,忘掉它吧!实际上,sizeof计算对象的大小也是转换成对对象类型的计算,也就是说,同种类型的不同对象其sizeof值都是一致的。这里,对象可以进一步延伸至表达式,即sizeof可以对一个表达式求值,编译器根据表达式的最终结果类型来确定大小,一般不会对表达式进行计算。如:
1 2 | sizeof(2);//2的类型为int,所以等价于sizeof(int); sizeof(2+3.14);//3.14的类型为double,2也会被提升成double类型,所以等价于sizeof(double); |
sizeof也可以对一个函数调用求值,其结果是函数返回类型的大小,函数并不会被调用,我们来看一个完整的例子:
1 2 3 4 5 6 7 8 9 10 11 | charfoo() { printf("foo()hasbeencalled.\n"); return'a'; } int main() { size_tsz=sizeof(foo()); //foo()的返回值类型为char,所以sz=sizeof(char),foo()并不会被调用 printf("sizeof(foo())=%d\n",sz); } |
sizeof的计算发生在编译时刻,所以它可以被当作常量表达式使用,如:
1 | Char ary[sizeof(int)*10];//ok |
所以我们最好还是认为sizeof是在编译期执行的,这样不会带来错误,让程序的可移植性强些。
基本数据类型的sizeof
这里的基本数据类型指short、int、long、float、double这样的简单内置数据类型,由于它们都是和系统相关的,所以在不同的系统下取值可能不同,这务必引起我们的注意,尽量不要在这方面给自己程序的移植造成麻烦。
一般的,在32位编译环境中,sizeof(int)的取值为4。
指针变量的sizeof
学过数据结构的你应该知道指针是一个很重要的概念,它记录了另一个对象的地址。既然是来存放地址的,那么它当然等于计算机内部地址总线的宽度。所以在32位计算机中,一个指针变量的返回值必定是4(注意结果是以字节为单位),但是,在64位系统中指针变量的sizeof结果为8。
1 2 3 4 5 6 7 8 9 10 | char*pc="abc"; int*pi; string*ps; char**ppc=&pc; void(*pf)();//函数指针 sizeof(pc);//结果为4 sizeof(pi);//结果为4 sizeof(ps);//结果为4 sizeof(ppc);//结果为4 sizeof(pf);//结果为4 |
指针变量的sizeof值与指针所指的对象没有任何关系,正是由于所有的指针变量所占内存大小相等
数组的sizeof
数组的sizeof值等于数组所占用的内存字节数,如:
char a1[] = "abc"; int a2[3];sizeof( a1 ); // 结果为4,字符末尾还存在一个NULL终止符 sizeof( a2 ); // 结果为3*4=12(依赖于int)
一些朋友刚开始时把sizeof当作了求数组元素的个数,如今你应该知道这是不对的,那么应该怎么求数组元素的个数呢Easy,通常有下面两种写法:
1 2 | intc1=sizeof(a1)/sizeof(char);//总长度/单个元素的长度 intc2=sizeof(a1)/sizeof(a1[0]);//总长度/第一个元素的长度 |
写到这里,提一问,下面的c3,c4值应该是多少呢
1 2 3 4 5 6 7 8 | voidfoo3(chara3[3]) { intc3=sizeof(a3);//c3== } voidfoo4(chara4[]) { intc4=sizeof(a4);//c4== } |
也许当你试图回答c4的值时已经意识到c3答错了,是的,c3!=3。这里函数参数a3已不再是数组类型,而是蜕变成指针,相当于char* a3,为什么仔细想想就不难明白,我们调用函数foo3时,程序会在栈上分配一个大小为3的数组吗不会!数组是“传址”的,调用者只需将实参的地址传递过去,所以a3自然为指针类型(char*),c3的值也就为4。
结构体的sizeof
这是初学者问得最多的一个问题,所以这里有必要多费点笔墨。让我们先看一个结构体:
1 | structS1{charc;inti;}; |
问sizeof(s1)等于多少聪明的你开始思考了,char占1个字节,int占4个字节,那么加起来就应该是5。是这样吗?你在你机器上试过了吗?也许你是对的,但很可能你是错的!VC6中按默认设置得到的结果为8。
我们来好好琢磨一下sizeof的定义——sizeof的结果等于对象或者类型所占的内存字节数,
这就是传说中的字节对齐啊!一个重要的话题出现了。
为什么需要字节对齐计算机组成原理教导我们这样有助于加快计算机的取数速度,否则就得多花指令周期了。为此,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。
让我们交换一下S1中char与int的位置:
1 | structS2{inti;charc;}; |
看看sizeof(S2)的结果为多少,怎么还是8再看看内存,原来成员c后面仍然有3个填充字节,这又是为什么啊别着急,下面总结规律。
1)结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2)结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal padding);
3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
联合体的sizeof
结构体在内存组织上是顺序式的,联合体则是重叠式,各成员共享一段内存,所以整个联合体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是复合类型,这里,复合类型成员是被作为整体考虑的。
所以,下面例子中,U的sizeof值等于sizeof(s)。
1 2 3 4 5 6 | unionU { inti; charc; S1s; }; |
与strlen区别
strlen(char*)函数求的是字符串的实际长度,直到遇到第一个'\0',然后就返回计数值,且不包括'\0'。
charaa[10];cout<<strlen(aa)<<endl; //结果是不定的,因为未初始化,'\0'在内存中的位置不确定
char aa[10]={'\0'};cout<<strlen(aa)<<endl; //结果为0
char aa[10]="jun";cout<<strlen(aa)<<endl; //结果为3
而sizeof()函数返回的是变量声明后所占的内存数,不是实际长度。
sizeof(aa) 返回10 int a[10]; sizeof(a) 返回40
1.sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。
该类型保证能容纳实现所建立的最大对象的字节大小。
2.sizeof是算符,strlen是函数。
4.数组做sizeof的参数不退化,传递给strlen就退化为指针了。
5.大部分编译程序在编译的时候就把sizeof计算过了是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因
1 2 3 | charstr[20]="0123456789"; inta=strlen(str);//a=10; intb=sizeof(str);//而b=20; |
6.strlen的结果要在运行的时候才能计算出来,是用来计算字符串的长度,不是类型占内存的大小。
7.sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。
8.当适用于一个结构类型时或变量, sizeof 返回实际的大小,当适用于静态的空间数组, sizeof 归还全部数组的尺寸。
sizeof 操作符不能返回被动态分派的数组或外部数组的尺寸
9.数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,
7.数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,
如:
1 2 | fun(char[8]) fun(char[]) |
都等价于 fun( char* )
char* str ="abacd";
sizeof(str)//结果 4--->str是指向字符串常量的字符指针,sizeof获得的是一个指针所占的空间,应该是长整型的,所以是4;
sizeof(*str)//结果 1--->*str是第一个字符,其实就是字符串的第一位'a' 所占的内存空间,是char类型的,占了 1 位;
strlen(str)=5 //--->若要获得这个字符串的长度,则一定要使用 strlen
在使用sizeof时,有一个很特别的情况,就是数组名到指针蜕变,
1 2 3 4 | charArray[3]={'0'}; sizeof(Array)==3; char*p=Array; strlen(p)==1;//sizeof(p)结果为4 |
看完以上你是否很清楚sizeof和strlen的区别了呢?还不明白的话,我们看下面几个例子:
第一个例子
1 | char*ss="0123456789"; |
sizeof(ss) 结果 4 ===》ss是指向字符串常量的字符指针
sizeof(*ss) 结果 1 ===》*ss是第一个字符
大部分编译程序 在编译的时候就把sizeof计算过了 是类型或是变量的长度
这就是sizeof(x)可以用来定义数组维数的原因
1 2 3 | charstr[20]="0123456789"; inta=strlen(str);//a=10; intb=sizeof(str);//而b=20; |
大部分编译程序 在编译的时候就把sizeof计算过了 是类型或是变量的长度
这就是sizeof(x)可以用来定义数组维数的原因
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | charstr[20]="0123456789"; inta=strlen(str);//a=10; intb=sizeof(str);//而b=20; charss[]="0123456789"; sizeof(ss)结果11===》ss是数组,计算到\0位置,因此是10+1 sizeof(*ss)结果1===》*ss是第一个字符 charss[100]="0123456789"; sizeof(ss)结果是100===》ss表示在内存中的大小100×1 strlen(ss)结果是10===》strlen是个函数内部实现是用一个循环计算到\0为止之前 intss[100]="0123456789"; sizeof(ss)结果400===》ss表示在内存中的大小100×4 strlen(ss)错误===》strlen的参数只能是char*且必须是以'\0'结尾的 charq[]="abc"; charp[]="a\n"; sizeof(q),sizeof(p),strlen(q),strlen(p); |
结果是 4 3 3 2