全面总结sizeof的用法(定义、语法、指针变量、数组、结构体、类、联合体、位域位段)
一、前言
编译环境是vs2010(32位)。
<span style="font-size:18px;">#include<iostream> #include<stdio.h> #include<string.h> using namespace std; typedef struct { int a; char b; }A_t; typedef struct { int a; char b; char c; }B_t; typedef struct { char a; int b; char c; }C_t; void main() { char*a=0; cout<<sizeof(<span style="color:#ff0000;">a</span>)<<endl;//4 cout<<sizeof(<span style="color:#ff0000;">*a</span>)<<endl;//1--这个能理解 cout<<sizeof(<span style="color:#ff0000;">A_t</span>)<<endl;//8 cout<<sizeof(<span style="color:#ff0000;">B_t</span>)<<endl;//8 cout<<sizeof<span style="color:#ff0000;">(C_t</span>)<<endl;//12 }</span>
为什么是这样的结果啊?
二、语法
sizeof有三种语法形式,如下:
1) sizeof( object ); // sizeof( 对象 );
2) sizeof( type_name ); // sizeof( 类型 );
3) sizeof object; // sizeof 对象;
三. 指针变量的sizeof
既然是来存放地址的,那么它当然等于计算机内部地址总线的宽度。所以在32位计算机中,一个指针变量的返回值必定是4(以字节为单位),在64位系统中指针变量的sizeof结果为8。
<span style="font-size:18px;">#include<iostream> #include<stdio.h> #include<string.h> using namespace std; int main() { char *a=0; char* pc = "abc"; int* pi; string* ps; char** ppc = &pc; <span style="color:#ff0000;">void (*pf)();// 函数指针</span> cout<<sizeof(<span style="color:#ff0000;">char</span>)<<endl; //1 cout<<sizeof(<span style="color:#ff0000;">a</span>)<<endl;//4 cout<<sizeof(<span style="color:#ff0000;">*a</span>)<<endl;//1 cout<<sizeof(<span style="color:#ff0000;">pc</span>)<<endl; //4(指针) cout<<sizeof(<span style="color:#ff0000;">pi</span>)<<endl;//4(指针) cout<<sizeof(<span style="color:#ff0000;">ps</span>)<<endl; //4(string型指针) cout<<sizeof(<span style="color:#ff0000;">ppc</span>)<<endl; //4(指针) cout<<sizeof(<span style="color:#ff0000;">pf</span>)<<endl;//4 }</span>
指针变量的sizeof值与指针所指的对象没有任何关系,正是由于所有的指针变量所占内存
大小相等,所以MFC消息处理函数使用两个参数WPARAM、LPARAM就能传递各种复杂的消息结构(使用指向结构体的指针)。
四.、数组的sizeof
数组的sizeof值等于数组所占用的内存字节数,如:
<span style="font-size:18px;">#include<iostream> #include<stdio.h> #include<string.h> using namespace std; int main() { char b1[]="123"; int b2[3]; <span style="color:#ff0000;">int c1=sizeof(b1)/sizeof(char); int c2=sizeof(b1)/sizeof(b1[0]); int c3=sizeof(b2)/sizeof(int);</span> int c4=sizeof(b2)/sizeof(b2[0]); cout<<sizeof(b1)<<" "<<c1<<" "<<c2<<endl;//4 4 4 cout<<sizeof(b2)<<" "<<c3<<" "<<c4<<endl;//12(3*4 依赖int) 3 3 }</span>
1.数组长度
char a1[] = "abc";
int a2[3];
sizeof( a1 ); // 结果为4,字符串末尾还存在一个NULL终止符
sizeof( a2 ); // 结果为3*4=12(依赖于int)
2.数组元素个数
int c1 = sizeof( a1 ) / sizeof( char ); // 总长度/单个元素的长度
int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 总长度/第一个元素的长度
3.数组“传址”(数组为函数参数)
我们可以思考一下,下面的c3,c4值应该是多少呢?
<span style="font-size:18px;">void foo3(char a3[3]) { int c3 = sizeof( a3 ); // c3 == } void foo4(char a4[]) { int c4 = sizeof( a4 ); // c4 == } </span>
也许当你试图回答c4的值时已经意识到c3答错了,是的,c3!=3。这里函数参数a3已不再是数组类型,而是蜕变成指针,相当于char* a3,为什么?仔细想想就不难明白,我们调用函数foo1时,程序会在栈上分配一个大小为3的数组吗?不会!数组是“传址”的,调用者只需将实参的地址传递过去,所以a3自然为指针类型(char*),c3的值也就为4。
五. 结构体的sizeof
结构体相对而言最容易碰到而且最容易出错。让我们先看一个结构体:
struct S1
{
char c;
int i;
};
编译得到结果为8!
我们来好好琢磨一下sizeof的定义——sizeof的结果等于对象或者类型所占的内存字节数,好吧,那就让我们来看看S1的内存分配情况:
S1 s1 = { a , 0xFFFFFFFF };
定义上面的变量后,加上断点,运行程序,观察s1所在的内存,你发现了什么?
以我的Vs为例,s1的地址为0x0012FF78,其数据内容如下:
0012FF78: 61 CC CC CC FF FF FF FF
发现了什么?怎么中间夹杂了3个字节的CC?看看MSDN上的说明:
When applied to a structure type or variable, sizeof returns the actual size,
which may include padding bytes inserted for alignment.
原来如此,这就是传说中的字节对齐啊!一个重要的话题出现了。
1.怎么判断内存对齐规则,sizeof的结果怎么来的,牢记如下3条规则(在没有#pragma pack宏的情况下):
(1)数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
(2)结构体作为成员:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(struct a 里存有struct b,b里有char,int,double等元素,那么b应该从8的整数倍开始存储)。
(3)收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
-
类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char
偏移量必须为sizeof(char)即1的倍数
int
偏移量必须为sizeof(int)即4的倍数
float
偏移量必须为sizeof(float)即4的倍数
double
偏移量必须为sizeof(double)即8的倍数
Short
偏移量必须为sizeof(short)即2的倍数
<span style="font-size:18px;">#include<iostream> #include<stdio.h> #include<string.h> using namespace std; typedef struct bb { int id; //[0]....[3] double weight; //[8].....[15] 原则1 float height; //[16]..[19],总长要为8的整数倍,补齐[20]...[23] 原则3 }BB; typedef struct aa { char name[2]; //[0] [1] int id; //[4]....[7] 原则1 double score;// [8]...[15] short t; //[16]...[17] BB b; //[24]...[47] 原则2、3 }AA; int main() { cout<<sizeof(BB)<<endl; //为24 cout<<sizeof(AA)<<endl; //为48 return 0; }</span>
2.带#pragma pack()
在代码前加上一句#pragma pack(1),bb就是4+8+4=16。Aa就是2+4+8+2+16=32.
六、联合体的sizeof
结构体在内存组织上是顺序式的,联合体则是重叠式,各成员共享一段内存,所以整个联合体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是复合类型,这里,复合类型成员是被作为整体考虑的。
所以,下面例子中,U的sizeof值等于sizeof(s)。
<span style="font-size:18px;">union U { int i; char c; AA s; };</span>
七、类的sizeof
1、空类的sizeof是1。空类是指没有成员的类,类中的函数不占空间,除非是虚函数。
如:
<span style="font-size:18px;"> class A { public: A(){} ~A(){} void fun(){} };</span>
sizeof(A)是1.
注:
<span style="font-size:18px;"> class A1 { public: A1(){} ~A1(){} void fun(){} char a[0]; };</span>
sizeof(A1)也是1.(VC6.0下编译)
2、若类中包含成员,则类对象的大小只包括其中非静态成员经过对齐所占的空间,对齐方式和结构体相同。如:
<span style="font-size:18px;">class A { public: int b; float c; char d; };</span>