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
的结果可以被用来作为定义静态数组的维数。