lol皎月女神出装:简要讲述C/C++中的操作符sizeof()

来源:百度文库 编辑:中财网 时间:2024/04/28 20:31:31

1.  sizeof是一个操作符,作用是返回一个对象或者类型所占的内存字节数
MSDN上的解释为:
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.
其返回值类型为size_t,在头文件stddef.h中定义。这是一个依赖于编译系统的值,一般定义为
typedef unsigned int size_t;

2.  一般的,在32位编译环境中,sizeof(int)的取值为4。

3.  指针来存放地址的,那么它当然等于计算机内部地址总线的宽度。所以在32位计算机中,一个指针变量的返回值必定是4(注意结果是以字节为单位),在64位系统中指针变量的sizeof结果为8。
指针变量的sizeof值与指针所指的对象没有任何关系。
由于所有的指针变量所占内存大小相等,因此MFC的消息结构通过指向结构体的指针参数传递。

4.  数组的sizeof值等于数组所占用的内存字节数
如下求数组元素的个数:
int n = sizeof( a1 ) / sizeof( a1[0] ); // 总长度/第一个元素的长度 a1代表数组
由于传的参数是指针,故c3!=3:
void foo3(char a3[3])
{
int c3 = sizeof( a3 ); // c3 == 4 因为是指针
}
调用函数时,程序会在栈上分配一个大小为3的数组吗?
不会!数组是“传址”的,调用者只需将实参的地址传递过去。
所以a3自然为指针类型(char*),c3的值也就为4。

5.  结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目。

结构体的sizeof需要考虑需要字节对齐

原因:这样有助于加快计算机的取数速度,否则就得多花指令周期了。为此,编译器默认会对结构体进行处理,让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。

准则:字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
3) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);

例子:
struct S1
{
char c;
int i;
};
//sizeof(S1)=8;
struct S2
{
int i;
char c;
};
//sizeof(S2)=8;
struct S3
{
char c1;
S1 s;
char c2
};
//sizeof(S3)=16;
解释一下S3。
第一步:S1的最宽简单成员的类型为int,S3在考虑最宽简单类型成员时是将S1“打散”看的,所以S3的
最宽简单类型为int,这样,通过S3定义的变量,其存储空间首地址需要被4整除,整个sizeof(S3)的值也应该被4整除。
第二步:考虑偏移量。
  c1的偏移量为0;
  s的偏移量为4;//考虑了最宽,通过填充进行了对齐,否则偏移量应该是1。
  c2的偏移量为4+sizeof(s)=12;
第三步:算上c2的所占大小,目前所占空间是13。但是应该被4整除。所以通过填充,sizeof是16。

通过上面的叙述,我们可以得到一个公式:
结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目,即:
sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( trailing padding )

编译器命令:
编译器的pack指令用来调整结构体对齐方式。不同编译器名称和用法略有不同,VC6中通过#pragma pack实现,也可以直接修改/Zp编译开关。
#pragma pack的基本用法为:
#pragma pack( n )
其中,n为字节对齐数,其取值为1、2、4、8、16,默认是8,如果这个值比结构体成员的sizeof值小,那么它就是该成员的偏移量。即结构体成员的偏移量应该取最小值,公式如下:offsetof( item ) = min( n, sizeof( item ) )
例子:
#pragma pack(push) // 将当前pack设置压栈保存
#pragma pack(2)// 必须在结构体定义之前使用
struct S1
{
char c;
int i;
};
struct S3
{
char c1;
S1 s;
char c2
};
#pragma pack(pop) // 恢复先前的pack设置
计算sizeof(S1)时,min(2, sizeof(i))的值为2,所以i的偏移量为2,加上sizeof(i)等于6,能够被2整除,所以整个S1的大小为6。
同样,对于sizeof(S3),s的偏移量为2,c2的偏移量为8,加上sizeof(c2)等于9,不能被2整除,添加一个填充字节,所以sizeof(S3)等于10。

还有一点要注意,“空结构体”(不含数据成员)的大小不为0,而是1。试想一个“不
占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢?因此,编译器为“空结构体”变量分配一个字节的空间用于占位存储。
例子:
struct S5 { };
sizeof( S5 ); // 结果为1

6.   含有位域的结构体的sizeof(不是很常用)

例子1:
struct BF1
{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
其内存布局为:
|_f1_|_f2__|____f3___|____|

|_|_|_|_|_|_ |_|_|_|_|_|_|_|_|_|_|
0      3        7 8             13      16


位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只
能从下一个字节开始。因此sizeof(BF1)的结果为2。
示例2:
struct BF2
{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
示例3:
struct BF3
{
char f1 : 3;
char f2;
char f3 : 5;
};
非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。

7.  联合体的sizeof
结构体在内存组织上是顺序式的,联合体则是重叠式,各成员共享一段内存,所以整个联合体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是复合类型,这里,复合类型成员是被作为整体考虑的。
所以,下面例子中,U的sizeof值等于sizeof(s)。
union U
{
int i;
char c;
S1 s;
};