sizeof是C++中的一个运算符(不是一个函数),它的作用被表述为:The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types). This keyword returns a value of type size_t. 简而言之,就是返回一个变量或类型在内存中所占的字节数。但正是这么一个看似简单的运算符,却成为了面试中的热门考点。这说明关于这个运算符其实有很多需要注意的地方。在这篇文章中,我就对这个运算符进行一个简要的总结。
一、指针,数组与sizeof
面试的时候,面试官经常将sizeof运算符与char*类型字符串或者char[]类型字符串综合起来考察。比如下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
在main函数中,我们依次定义了三个内容相似的字符串。首先对于str1,它是一个指针,指针大小在32位机器中恒定为4,所以输出为4;str2是字符数组,没有预定义大小,所以其大小由存储的字符串长度决定,字符串的长度为10,加上末尾的字符串结束符\0,共11个字符,所以输出11;str3是预定义大小的字符数组,不管内部存储的字符串长度是多少,其大小都是预定义的大小100,所以输出100。而对于函数getSize,我们需要了解的是,在C++中,任何数组作为函数参数传递进去后都会退化成指针,所以输出为4。
综上所述 ,输出依次为4、11、100和4。
二、struct和class在内存中的数据对齐
数据对齐这件事,我们平时好像不太关注,但是一提到sizeof操作符,就会不可避免地涉及到内存对齐的机制。之所以要进行内存对齐,是出于如下考虑:对于n字节的元素,它的首地址能被n整除时,才能在程序执行时获得最好的性能。对于类或者结构体,为了保证这一点,就需要使其内部的成员变量按照最长的成员变量的长度去对齐。举个例子:
1 2 3 4 5 6 7 8 9 10 11 | |
在上面的代码中,我们定义了两个结构体A和B,它们的成员变量内容相同,但是顺序不同。通过观察发现,A和B中最长的数据元素类型均为int,占4字节,所以两个结构体的成员变量均要以4字节为单位进行对齐。简而言之,就是成员变量按照顺序放入长度为4字节的位置中,如果能放得下,就继续往里放;如果放不下,就需要开辟一个新的位置。所以,A和B数据对齐的情况如下:
1 2 3 4 5 6 7 8 9 10 | |
而对于联合体union,相比于struct在内存上的顺序式组织,union是重叠式组织的,所以对一个union使用sizeof返回的是其最长成员的长度。
三、类和sizeof运算符
3.1 关于空类的继承:
1 2 3 4 5 6 7 8 9 | |
对一个空类使用sizeof结果为1,因为即使是空类,也需要在内存中占用一定位置,否则就不可寻址了,这是编译器做的一步特殊处理。所以sizeof(A)的结果为1。对于一个只有普通函数,没有成员变量的类使用sizeof,结果仍为1,因为类的普通函数不会占用类的每个实例的空间。所以sizeof(B)的结果为1。对一个多重继承的空类使用sizeof,输出仍然为1。所以sizeof(C)的结果为1。
3.2 关于有成员变量的类的继承:
1 2 3 4 5 6 7 8 9 10 11 | |
在上面这种情况下,C的实例不仅要存储自身定义的成员变量,还要存储其父类定义的成员变量。所以sizeof(C)的结果为12。
3.3 关于有虚函数的类:
1 2 3 4 5 6 7 8 9 10 11 | |
有虚函数的类会保存一个指向虚表的指针,在32位机器上指针的大小为4,所以sizeof(A)的结果为4。一个类无论有多少个虚函数,都只有一张虚表,所以sizeof(B)的结果也是4。而如果一个类有多重继承,那么它就会保存多张虚表。在上面的例子中,C的实例保存了两张虚表,每张虚表的大小是4,所以sizeof(C)的结果为8。
3.4 关于有static成员变量的类:
1 2 3 4 | |
静态成员变量是存放在全局数据区的,而sizeof运算符只计算栈中分配的大小,所以静态成员变量不会计算在内。sizeof(A)的结果为4。
四、sizeof和strlen的区别与联系
- 首先,
sizeof是运算符,而strlen是函数。 - 其次,
sizeof可以作用于变量名、类型名、表达式或者函数;而strlen只能使用char类型字符串做参数,且必须是以\0结尾。对于一个char数组,sizeof返回的是该数组的总大小,而strlen返回的是该数组实际对应的字符串的长度(末尾的\0不计算在内);对于一个char*字符串,sizeof在32位机器上恒定返回4,而strlen仍然返回字符串的实际长度。 - 最后,大部分程序在编译阶段做类型检测的时候就把
sizeof的值计算过了(参考如下代码),而strlen必须是运行时才会计算出来。
1 2 3 4 5 6 7 8 9 10 11 | |
上述代码中,在main函数的第一行,sizeof(add())会在编译时刻被替换为sizeof(int)然后得出结果4,不需要在运行时调用add函数。同理,x=6这句表达式也是不被执行的。所以在这两个表达式执行完成后,x的值仍然为0。另外,由于在编译时刻已经确定了值,所以sizeof的结果可以被用来作为定义静态数组的维数。