sizeof是C/C++中的关键字,它是一个运算符,其作用是取得一个对象(数据类型或者数据对象)的长度(即占用内存的大小,以byte为单位)。
在C++中,变量和自定义类型可以不用加(),内置类型要加。
需要注意的点包括:
1)它是运算符,不是函数;
2)sizeof不能求得void类型的长度;
下面两种情况都会编译出错,
sizeof (void );
void f(){};
sizeof (f());
3)sizeof求指针的长度:
sizeof(void *) = sizeof(int*) = sizeof(void*) = sizeof(double*)
在32位系统下,结果为4。
4)sizeof求得静态分配内存的数组的长度:
int a[10];
sizeof (a) = 10 * sizeof (int) = 40;
注意结果以字节为单位,所以不要忘了乘以4。
注意数组退化为指针(在传递数组时,为避免拷贝太多数据而导致效率太低,只传递数组的首地址)的情况:
void fun(int a[10])
{
int n = sizeof(a); //结果为4,常见的陷阱
}
注意上面提到的是是静态分配的数组,这是C/C++很特殊的一种内置类型。
对于动态分配的数组,是没办法用sizeof求其大小的,因为传递给sizeof的操作数是指针:
int* a = new int[10]; int n = sizeof (a); //结果为4
类似的:
char *ss = "0123";
sizeof(*ss)的结果是1。
其实应该说sizeof能求静态分配的数组是特殊的情况,深究下去就要涉及sizeof的实现了。
还有一种情况就是对于外部数组变量:
extern arrayA[];
extern arrayB[10];
cout<
cout<
主要是因为sizeof在编译阶段就完成对静态数组的求值,而C/C++以源程序文件(*.c,*.cpp)为单位进行编译,上面arrayA[]在哪个源文件定义、元素个数多少是要等链接时才知道的。
5)当表达式作为sizeof的操作数时,它返回表达式的计算结果的类型大小,但是它不对表达式求值!
int i = 0, j = 1;
cout<< sizeof(++i);
cout<
cout<< sizeof(i+j);
cout<< sizeof(j=i+j);
cout<
cout<< sizeof(j+d);
cout<< sizeof(j=j+d);
结果为4044184。
只要熟悉表达式求值过程就表达式的值,就很容易写成结果。
而且这些情况,sizeof都是在编译阶段求值的,可以猜测是在语义分析后执行的(只是猜测),在语法分析建立语法树后,语义分析做的一个重要工作就是类型检查,对于表达式 j+d 进行了自动类型转换,计算结果类型为double。另一方面,既然是在编译阶段,sizeof表达式肯定不会改变变量的值。
6)sizeof可以对函数调用求大小,并且求得的大小等于返回类型的大小,但是不执行函数体(直接由其返回类型取代)!
和5)类似,只要理解在编译阶段求值就OK了。
7)sizeof求得的结构体(及其对象)的大小并不等于各个数据成员对象的大小之和!
这个与成员对齐有关,常见的求struct、union、struct/union组合结构的大小的题目,在VS下跑一跑,看一下内存分布就知道了。
而且要记住:
struct和class都要对齐,可以利用#pragma pack(n)设定按n字节对齐
8)sizeof不能用于求结构体的位域成员的大小,但是可以求得包含位域成员的结构体的大小!
包含位域成员的结构体在嵌入式编程中很常见,为了节省存储空间:
struct
{
bool a:1;
char b:3;
char c:4;
}state;
cout<<sizeof state; 的输出是1;对结构体求值还是没问题的。
而cout<<sizeof state.a; 则不能通过编译:sizeof的操作数不能是位域,原因很简单,最开始说过了,sizeof是以字节为单位求值的。
如果将state中成员b的位域改为4个比特,cout<<sizeof state; 的输出就变为2。
顺便写一下,如果改成:
typedef struct s
{
bool a:1;
char b:4;
char c:4;
}state;
且有:
state s1;
s1.a=0; s1.b=7; s1.c=4;
其内存分布:低字节 _ _ _ 0 1 1 1 0,高字节 _ _ _ _ 0 1 0 0
9) c99标准支持变长数组定义,某些编译器也对其进行了支持,如dev C++ 5.6,不过VS2010没有。
sizeof的实现与编译器有关,在支持变长数组的编译环境中,
int n = 3;cout<
其输出为164,因为可变长度的数组的长度需要在其长度表达式求值之后才能确定大小,++n执行了,因此这种情况下,sizeof是在运行阶段执行的。
10)sizeof求“多字节字符常量”(multi- character character constant):
这是C95后提供的wchar_t类型,它限制在单引号中包含2至4个字节,有点偏,可能嵌入式中用得多,
sizeof('ab') == sizeof('abc') == sizeof('abcd') == 4
如果用一个整形变量存放,'abcd'对应的是0x61626364。
11)sizeof求类、对象的大小,这个也很容易出错
class A
{
private:
int a;
static int b;
};
sizeof计算栈中分配的大小,因此sizeof A的结果是4!
class A
{
public:
virtual void foo(){cout<<"A"<
private:
char c[3];
};
答案为8,注意虚函数表指针及对齐!
当一个类A中没有任何成员变量与成员函数 ,这时sizeof(A)的值是多少?
肯定不是零,VS下面是1。编译器为什么没有让它为零?为了确保类的实例有唯一的地址。设想一下,如果定义class A[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分 A[0],A[1]…了。
关于sizeof的实现,有网友提到:
sizeof是在编译的时候,查找符号表,判断类型,然后根据基础类型来取值的,如果是struct则是看类型声明符号表来判定,如果字符串则是通过常量表来判断,具体可以参考编译原理的符号表管理章节。
sizeof的两个精巧的宏实现:
非数组的sizeof:
#defne _sizeof(T) ( (size_t)((T*)0 + 1))
数组的sizeof:
#define array_sizeof(T) ( (size_t)(&T+1) - (size_t)(&T) )
要知道sizeof返回结果类型是size_t,至于具体是unsigned int还是其他类型,要看编译器的typedef了。
看起来复杂一点的题目:
double* (*a)[3][6];
cout<<sizeof(a)< // 4
cout<<sizeof(*a)< // 72
cout<<sizeof(**a)< // 24
cout<<sizeof(***a)< // 4
cout<<sizeof(****a)< // 8
struct xx //16字节
{
long long a;
char b;
char c[2];
int d;
};
struct xx //24字节
{
long long a;
char b;
int d;
char c[2];
};
用途:
1)内存分配
调用malloc和memcpy等函数时都需要知道某种类型所占的单元字节
2)类型扩充
windows中有很多结构类型就有一个专门的字段用来存放该类型的字节大小