马上又要一波面试,有些东西就得再好好捋捋,先从 sizeof 开始吧,开始之前,先放出参考连接:
字节对齐:blog.csdn.net/21aspnet/ar…
百度百科:baike.baidu.com/view/107866…
定义
sizeof 是C/C++中的一个操作符,不是一个函数,作用是返回一个对象或者类型所占的内存字节数。返回类型为 size_t ,该类型在头文件 stddef.h 中定义,该值依赖编译系统的值,一般定义为:typedef unsigned int size_t 。
使用
sizeof 有三种语法形式
sizeof(object); //计算对象
sizeof(type_name); //计算类型
sizeof object; //计算对象
示例如下:
#include
using namespace std;
int main() {
int i;
std::cout<
用 g++ 编译,系统是 Ubuntu 64 位,输出的结果都是4。
sizeof 的用法可以进一步延伸至表达式,编译器根据表达式的最终结果类型来确定大小,一般不会对表达式求值;也可以对函数求值:
sizeof(1); //相当于sizeof(int)
sizeof(1+1.1); //相当于sizeof(double)
sizeof(foo()); //如果sizeof的返回值是char,则相当于sizeof(char),如果foo函数的返回值为void,则会报错
sizeof 的常量性
sizeof 的计算发生在编译时刻,所以可以把它当做常量表达式使用。
指针变量的 sizeof
指针代表了计算机内部地址总线的宽度,所以在32位的计算机上,返回值一定是4,64位的计算机上的返回值为8。
数组的 sizeof
数组的 sizeof 等于数组所占的内存字节数
char a[] = "ab";
sizeof(a); //大小为3,包含'\0'结束符
int b[4]; //大小为16
结构体的 sizeof
为了加快计算机的存取速度,通常要对结构体进行字节对齐的处理,所以通常 sizeof 计算的结构体的大小比结构体里单个数据类型加和后的大小要大。
字节对齐一般满足三个准则:
- 结构体变量的首地址能够被其最宽基本类型成员的大小(这一点不够严谨,在后面会进行纠正)所整除
- 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,不够的话就进行字节填充
- 结构体的总大小为结构体最宽基本类型成员大小的整数倍,有需要会进行填充
补充说明 :其中 基本类型成员 指的是 char、short、int、long、float、double 这些类型;如果结构体中包含结构体,在找最宽基本类型成员的时候不能把它看成一个整体,而应该看这个结构体里面的子成员;在计算偏移量时,要把结构体中的结构体看做一个整体;举例如下:
struct SA
{
int i;
char c;
};
struct SB
{
double f;
struct SA sa;
int arr[3];
};
int main() {
cout<<"sa的大小为:"<
其中 offsetof
函数是计算一个结构体中成员的偏移量,头文件为 stddef.h
。函数接收两个参数,第一个参数是结构体的名字,第二个参数是结构体中成员的名字,结果就是对应成员在结构体中(字节对齐后)的偏移量。
先给出对应的结果(Ubuntu 64位,g++编译器):
SA的大小为:8
SB的大小为:32
SA中i的偏移量是:4
SA中c的偏移量是:0
SB中f的偏移量是:0
SB中sa的偏移量是:8
SB中arr的偏移量是:16
对于第一个结果:SA中最宽基本类型成员为 i ,int 类型,长度为4,所以c需要不上3个字节进行对齐,所以结构是8;第二个结果:最宽基本类型成员为 f ,double 类型,8个字节,这里需要注意的是,此时若不考虑 SB 的字节填充(SB中的SA已经填充过了),则大小应该为 28 ,根据准则三,最宽字节 8 不能整除 28,所以需要在最后,也就是 arr 的后面再补充4个字节,结果为 32 ;后面的偏移量,只要知道哪个填充了多少个字节,自然就知道结果了。
总结就是结构体的大小等于最后一个成员的偏移量加上它本身的大小再加上末尾的填充字节。
到这里结构体的字节大小基本上分析完了,但是还有一个很重要的东西可能会让上面的结果看上去自相矛盾,先看例子:
#pragma pack(push) //保存当前设置
#pragma pack(2) //调整对齐大小为2
struct SA
{
char c;
int i;
};
struct SB
{
double f;
struct SA sa;
int arr[3];
};
#pragma pack(pop) //恢复之前的设置
int main() {
cout<<"sa的大小为:"<
和上面代码的区别主要是在结构体的前后进行了#pragma pack()
的设置,暂时先不管它的作用,先来看一下结果:
SA的大小为:6
SB的大小为:26
SA中i的偏移量是:2
SA中c的偏移量是:0
SB中f的偏移量是:0
SB中sa的偏移量是:8
SB中arr的偏移量是:14
是不是很奇怪?这就得引入另外一个概念:字节对齐值,它来指定在进行对齐的时候,到底按几字节对齐。之前的代码中,我们关于结构体的对齐规则是按照基本成员中最长的成员对齐,所以可以暂时理解为 SA 的对齐值为4,SB 的对齐值为8,并且结构体总长度要能分别被4和8整除,在这里就要纠正一下三个准则中准则一的说法,不是要被结构体中最宽字节成员的大小整除,而是要被有效对齐字节整除,对于这个例子,手动设置了字节对齐值为2,那所有被设置过的结构体的有效字节对齐数都是2。所以再重新按照原来的规则计算,就能得到上面输出的结果。关于 对其值 这个概念,更加详细的推荐看看这篇博客,关于字节对齐讲解的非常详细。
联合体的 sizeof
所谓联合体,就是各成员共享一段内存,所以整个联合体的 sizeof 是每个成员 sizeof 的最大值,另外,当联合体中是复合类型时,其中的非基本类型成员被当做整体考虑。
说说与 strlen 的区别
- 定义不同,strlen 函数求的是字符串的实际长度,直到遇到第一个'\0',然后计数器返回,且不包括'\0',而 sizeof 求的是变量对应类型所占的内存数,和实际内容长度没有关系。
- strlen 是函数,sizeof 是运算符。
- strlen 只能作用于 char* 类型,且必须以"\0"结尾,sizeof 还可以作用于函数。
- 作用于数组时,sizeof 的参数不退化,而 strlen 中数组退化为指针。
- 基本上 sizeof 的结果在编译时就计算出来了,而 strlen 在运行时才知道。