1、sizeof操作符的结果的类型是unsigned int,这种类型保证能够容纳实现所建立的最大对象的字节大小
2、sizeof是运算符,strlen是函数
3、sizeof可以用类型做参数,strlen只能用char*做为参数,无符号的字符型,切记,而且该字符串必须是以‘\0‘作为结尾的。
此外sizeof还可以用函数作为参数,返回的是函数返回值类型的大小
4、数组做sizeof的参数不会发生退化,但是数组作为strlen的参数的时候就退化成了指针。
举个例子:
char var[10];
int test(char var[])
{
return sizeof(var);
}
返回的值是4,因为var不再表示一个数组了,他只表示一个指针,对指针用sizeof很显然是4
5、大部分的编译程序的时候就把sizeof计算过了,是类型或是变量的长度。这就是sizeof(x)可以用来定义数组维度的原因
int _tmain(int argc, _TCHAR* argv[])
{
char str[20] = "0123456789";
int a = strlen(str);
int b = sizeof(str);
cout << "a: " << a << " b:" << b << endl;
while (1);
return 0;
}
有人就有疑惑了,既然strlen计算是以'\0'结尾,也就是说当strlen检测到0的时候就结束计算字符串的长度,为什么上面"0123456789"在开始出就没有结束计算呢?请查看ASCII码表
6、strlen的结果要在运行的时候才能够计算出来,用来计算字符串的长度,而不是类型占内存的大小。
7、sizeof后如果是类型,那么必须加括号,如果不是类型是个变量的话就可以不用加括号,这个是因为sizeof是个操作数而不是一个函数。
举个例子吧
int _tmain(int argc, _TCHAR* argv[])
{
char str[20] = "0123456789";
int a = strlen(str);
int b = sizeof(str);
cout << "int: " << sizeof(int)<< " b:" << sizeof b << endl;
while (1);
return 0;
}
8、当使用了一个结构类型或变量时,sizeof返回实际的大小。当使用一静态的空间数组时,sizeof返回全部数组的尺寸。sizeof不能返回被动态分配数组或外部数组的尺寸
9、数组作为参数传给函数的时候传递的是指针而不是数组,传递的是数组的首地址,在C++里面,传递数组永远都是传递数组首地址元素的指针,编译器不知道数组的大小,只能通过使用memcpy将数组复制出来,长度由另外一个参数传递进去
举个例子:
void func(unsigned char *str,int len)
{
unsigned char *buf = new unsigned char [len+1];// '\0' 你懂的
memcpy(buf,str,len);
}
10、我记得前面我们用过sizeof计算结构体,联合体,这里重新提一下,计算结构变量的大小要考虑到数组对齐的问题,为了使CPU存取和读取数据的速度达到最快,C++在处理数据的时候经常把结构变量中的成员的大小按照4或者8的倍数计算,这就是数据对齐。
11、sizeof操作符不能用于函数的类型,不完全类型或为字段,不完全类型指具有位置存储大小的数据的类型,如未知存储大小的数组,未知内容的结构体或者联合体,以及void类型。
例子分析:
int _tmain(int argc, _TCHAR* argv[])
{
char *ss1 = "0123456789";
char ss2[] = "0123456789";
char ss3[100] = "0123456789";
cout << "sizeof(ss1):" << sizeof(ss1) << "因为ss是char类型的指针,指针在windows下都是用32位表示的,所以长度应该是4个字节"<<endl;
cout << "sizeof(*ss1):" << sizeof(*ss1) << "因为*ss1代表的是数组ss1的第一个元素,而且是char类型,所以占用的内存大小应该只有1个字节" << endl;
cout << "sizeof(ss2):" << sizeof(ss2) << "这个就表示的整个数组了,输出的结果是10+1 = 11因为包含一个‘\0’" << endl;
cout << "sizeof(*ss2):" << sizeof(*ss2) << "同理,一个字节" << endl;
cout << "sizeof(ss3):" << sizeof(ss3) << "长度已经定了,是100个字节" << endl;
cout << "strlen(ss3):" << strlen(ss3) << "这个长度是10,传递进去的是数组的地址,计算以'\0'结尾" << endl;
while (1);
return 0;
}
12、sizeof的使用场合
主要的用途是与存储分配和I/O系统那样的例程通信
void *malloc(size_t size);动态分配存储空间
size_t fread(void *ptr,size_t size,size_t nmemb,FILE * stream);I/O系统操作,学过linux驱动的应该很清楚这个fread函数
查看某种类型的对象在内存中所占用的字节
void *memset(void *ptr,size_t size,size_t nmemb,FILE * stream)给某段内存空间清0
动态分配内存空间,告诉内存需要多大的内存
便于一些类型的扩充,在windows中有很多结构类型就有一个专门的字段用来存放该类型的字节大小
由于操作数的字节数在实现是可能出现变化,建议在设计操作数字节大小的时候用sizeof代替常量计算
如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小
13、如果你写的是这样的代码,会怎么样呢?
int a = 1;
sizeof(a = 6);
cout << a;
为什么是1呢?
上面我说过,sizeof不是函数,是个运算符,它是个类似宏定义的特殊关键字。sizeof()括号内的内容在编译过程中是不被编译的,而是被替代的,这里的替代的意思就是,比如上面的sizeof(a = 6),则会被替换称为sizeof(int),打印出的结果是4,所以sizeof里面的语句是不会执行的,所以a的值还是1
14、如果你想使用sizeof计算类的大小,怎么搞
对类使用sizeof操作符的时候,一般计算的就是类中所有成员的sizeof大小之和,但是在下面出现的两种情况的时候要注意:
①类中含有虚成员函数的时候
class B
{
public:
float a;
}
因为B类的成员只有float类型,所以求所有类成员的和就是类B占用内存的大小,所以sizeof(B) = sizeof(float)
如果有普通函数呢?
class B
{
public:
float a;
void func1(){ cout << "我不占用内存"; }
};
结果还是一样的,普通函数并不占用内存空间。
如果有虚函数呢?
class B
{
public:
float a;
virtual void func1(){ cout << "我占用内存"; }
};
输出为8字节,而不是4个字节,这是因为在类中隐藏了一个指针,该指针指向虚函数表,正因为如此,使得C++能够支持多态,即在运行时绑定函数的地址。
如果有多个虚函数呢?
我们将函数修改如下:
输出和上面一样,因为增加的只是一个指向虚函数表的指针而已,这两个虚函数都在这个虚函数表上面,所以不需要增加额外的内存空间了。
②、如果类中没有任何的成员变量也没有任何的虚函数的时候又是怎么样的情况呢
我们还是写例子把
class B
{
public:
void func(){ cout << "hello world"; }
};
输出为1
这样解释,在早期的C++编译器中,这个值为0,然后当创建这样的对象的时候,他们与紧接着他们后面的对象有相同的地址。比如
B b;int a;
那么b和a就有相同的地址,这样的话操作对象b地址的操作就会影响变量a,所以现在大多数编译器中,该值得大小都为1,如果你用这段代码测试输出的结果为0的话那么恭喜你,你中大奖了。
综上总结一下吧,如果类中有虚函数,则sizeof的值为类的数据成员的大小+指向虚函数表的指针的大小,如果是派生类的话,那么还要加上他的基类数据成员的大小,如果是多重继承的话,那么还要加上各个基类的虚函数表指针
举例子吧:
class A
{
virtual void a(){}
};
class B :public virtual A
{
char b_data[3];
public:
virtual void b(){}
};
class C :public virtual B
{
char c_data[3];
public:
virtual void c(){}
};
int _tmain(int argc, _TCHAR* argv[])
{
cout << "class A size is:" << sizeof(A) << endl;
cout << "class B size is:" << sizeof(B) << endl;
cout << "class C size is:" << sizeof(C) << endl;
while (1);
return 0;
}
分析第一个:不分析了
分析第二个:对于class B是由class A继承而来,他不但继承了class A 的虚函数,而且还拥有自己的虚函数。那么class B中首先有用一个虚函数列表指针,指向自己的虚函数列表。接下来就来看继承了,首先通过加入一个虚类指针用来指向父类,然后还要包含父类的所有内容。
所以sizeof(B) = b的虚函数指针 + 类B的成员变量大小 + 指向父类的虚类指针 + 类A的大小
= 4 + 4 + 4 + 4 = 16
分析第三个:对于class C是由class B继承而来,他首先也有自己的指向虚函数列表的指针,然后是自己的成员变量的值,然后呢就是虚指针指向父类,然后指向父类的所有内容
所以sizeof(C) = 4+4+4+16 = 28
为什么成员变量是4个字节呢?我们以前提到过内存对其的内容
举个例子如下,求sizeof之后的内容是一样的
class A
{
virtual void a(){}
};
class B :public virtual A
{
char b_data[4];
public:
virtual void b(){}
};
class C :public virtual B
{
char c_data[4];
public:
virtual void c(){}
};
int _tmain(int argc, _TCHAR* argv[])
{
cout << "class A size is:" << sizeof(A) << endl;
cout << "class B size is:" << sizeof(B) << endl;
cout << "class C size is:" << sizeof(C) << endl;
while (1);
return 0;
}
这个人写得还是很不错的很值得看~~~
虚基础之单继承时的内存布局图
class A的情况太简单,没问题。从class B的内存布局图可以得出下面的结论。
1、vf_ptr B放在了类的首部,那么如果要想直接拿memcpy完成类的复制是很危险的,用struct也是不行的。
2、vbtbl_ptr_B,为什么不是先前我描述的vbptr_B_A呢?因为这个指针与我先前猜测的内容有很大区别。这个指针指向的是class B的虚类表。看看VB table,VB table有两项,第一项为FFFFFFFC,这一项的值可能没啥意义,可能是为了保证虚类表不为空吧。第二项为8,看起来像是class B中的class A相对该vbtbl_ptr_B的位移,也就是一个offset。类似的方法在C++ Object Model(P121)有介绍,可以去看看。
class C的内存布局就比较复杂了,不过它的内存布局也更一步说明我对vbtbl_ptr_B中的内容,也就是虚类表的理解是正确的。不过值得关注的是class B中的class A在布局时被移到前面去了,虽然整个大小没变,但这样一来如果做这样的操作 C c; B *b;b=&c;时b的操作如何呢?此时只要从c的虚类表里获得class B的位置既可赋值给b。但是在构建class C时会复杂一些,后面的使用还是非常简单的,效率也比较高。class A的内存布局被前移可能是考虑倒C的虚继承顺序吧。
我们继续话题:
上面我们提到过,空类所占用的空间是1对吧,那么空类的继承类呢?
我们还是继续做我们的实验吧
class A
{
};
class B : public A
{
};
还是1,说明空类的继承还是1,呵呵
要是多重继承呢?
class A1
{
};
class A2
{
};
class B : public A1,public A2
{
};
int _tmain(int argc, _TCHAR* argv[])
{
cout << sizeof(B);
while (1);
return 0;
}
还是1~~