目录
sizeof的具体应用场景
- 查看某个类型对象的所占内存空间的单位字节。
- 在动态分配一对象的时候,可以让系统知道要分配多少内存。
- 便于一些类型的扩充,在一些平台上(比如windows、python),创建一个变量,这个变量所存放的内存多少,作为一个属性直接放在了这个变量的属性当中。
- 一些操作数在用的时候,会实时的出现变化,所以,用sizeof可以实时的观察和记录占用内存情况。
- 如果sizeof所进行的对象,是函数或数组的形参,那么sizeof会直接返回其指针的大小。
常用数据类型大小
- int类型:4bit
- short类型:2bit
- long类型:4bit
- char类型:1bit
- double类型: 8bit
- bool类型:1bit
- float类型:4bit
- 指针类型:4bit
- 字符串类型:"char str[]="hello";",其长度要在最后加1(因为最后还有个"\n"符号存在占位),(5+1=6)
总之一句话,大部分都是4bit,最长的也就是8bit(double类型),最小的是1bit(char和bool)。
类对象所占空间的大小
首先要明白一件事,就是类所创建的对象的大小,并不是简单的将类中所有的变量进行累加后得来的,而是采用补齐的方式计算得来的!
类的大小必须是其内部成员的最大元素大小的整数倍(本质上,是一个类中的数据存放的时候,第一个数据成员存放在offset为0的地方,那么,以后的数据成员,都是存放的起始位置要从上一个成员大小的整数倍后开始存储)。
#include <stdio.h>
#include <iostream>
class A
{
public:
int i;
};
class B
{
public:
char ch;
private:
};
class C
{
public:
int i;
short j;
private:
};
class D
{
public:
int i;
short j;
char ch;
private:
};
class E
{
public:
int i;
int ii;
short j;
char ch;
char chr;
private:
};
class F
{
public:
int i;
int ii;
int iii;
short j;
char ch;
char chr;
private:
};
int main()
{
printf("sizeof(int) = %d\n", sizeof(int));
printf("sizeof(short) = %d\n", sizeof(short));
printf("sizeof(char) = %d\n", sizeof(char));
printf("sizeof(A) = %d\n", sizeof(A));
printf("sizeof(B) = %d\n", sizeof(B));
printf("sizeof(C) = %d\n", sizeof(C));
printf("sizeof(D) = %d\n", sizeof(D));
printf("sizeof(E) = %d\n", sizeof(E));
printf("sizeof(F) = %d\n", sizeof(F));
system("pause");
return 0;
}
运行如下:
A类包括:1个int->sizeof(A) = sizeof(int) = 4;
B类包括:1个char->sizeof(B) = sizeof(char) = 1;
C类包括:1个int(4字节)+1个short(2字节)->sizeof(C) = 4+2 = 6,因为6不是4的整数倍,所以,补2,得8;
D类包括:1个int(4字节)+1个short(4字节)+1个char(1字节) ->sizeof(D)= 4+(2+1) =7, 因为7不是4的整数倍,所以补1,得8;
E类:4+4+2+1+1 = 12,是4的整数倍,得12;
F类:4+4+4+2+1+1 = 16,是4的整数倍,得16;
判断结果,看加和的结果,是不是其中最大加数的整数倍,如果是,就可以了,如果不是,就补最小的数,使其成为最大加数的整数倍。
含有虚函数的类对象的空间大小
普通函数不占用内存,但只要有虚函数,就会占用一个指针大小的内存(这是虚函数中包含一个虚函数表的缘故),所以,类中有多少个虚函数,就相当于有了几个指针变量(4字节)。
如下,两套继承函数,一套是带虚函数,一套不带,它俩的区别就很明显:
#include <stdio.h>
#include <iostream>
using namespace std;
class Base
{
public:
Base(int x) :a(x)
{
}
void print()
{
printf("base\n");
}
private:
int a;
};
class Derived:public Base
{
public:
Derived(int x) :Base(x - 1), b(x)
{
}
void print()
{
printf("derived\n");
}
private:
int b;
};
class A
{
public:
A(int x) :a(x)
{
}
virtual void print()
{
printf("A");
}
private:
int a;
};
class B:public A
{
public:
B(int x) :A(x - 1), b(x)
{
}
virtual void print()
{
printf("B");
}
private:
int b;
};
int main()
{
Base obj1(1);
printf("sizeof(Base obj1):%d\n", sizeof(obj1));
Derived obj2(2);
printf("sizeof(Base obj2):%d\n", sizeof(obj2));
A a(1);
printf("sizeof(A a):%d\n", sizeof(a));
B b(2);
printf("sizeof(B b):%d\n", sizeof(b));
system("pause");
return 0;
}
需要说明的是,下面的是在x86的环境下编译,结果是符合预想的:
对于Base,里面有一个int类型,故Base类所创建的对象大小就是4字节;对于Derived,里面有个int类型,而且这个类是从Base那继承过来的,所以在Base的基础上加4,即8字节;
同理,A的大小就是4+4(int类型+一个虚函数) =8;那么B就在A的基础上(这个时候A并没有调用虚函数,所以不能算上虚函数的那4个字节)也加上了一个Int类型和虚函数:4+4+4=12。
但如果是在x64位下运行:
结果就和之前的不一样了(具体原因,留待后查)。
注:如果类包含静态变量,静态变量所存放的内存空间和类中的局部变量所存在的内存空间并不在一块儿,所以,当用sizeof去计算包含静态变量的类的时候,会发现,没有算静态变量所占的大小。
sizeof和strlen的区别
- sizeof是操作符,strlen是定义的函数。
- sizeof可以用类型作参数,比如sizeof(int),但strlen不行,只能用strlen(char* a),而且,strlen是专门测量字符串的,对象必须以”\0“结尾。
- 数组作sizeof的参数的时候,是不会退化成指针的,但作函数的参数的时候,会退化为一个指针
- sizeof在编译过程中就把结果(因为是算类型)计算出来了,但strlen必须是在运行中才可以出结果(因为是算字符串的长度)。
- sizeof在使用的时候,如果后面是一个类型的,就需要加括号:sizeof(int),但如果是个变量,就不需要加括号了:sizeof a,这是因为sizeof是个操作符,不是函数。
如果是计算指针指向的字符串的长度,必须用strlen,
字符串用数组来存放
比如:
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
char str[20] = "0123456";
int a = sizeof(str);
printf("a:%d\n", a);
int b = strlen(str);
printf("b:%d\n", b);
system("pause");
return 0;
}
上面就发生了错乱了,牵扯到计算字符串的长度时候,宜用strlen。
字符串用指针来指向
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
const char* ss = "0123456";
int a = sizeof(ss);
printf("a:%d\n", a);
int b = strlen(ss);
printf("b:%d\n", b);
system("pause");
return 0;
}
运行结果:
用sizeof来计算的话,还是不准,会多出一个“\0”的字符来。
总之一句话,sizeof不能用来计算字符串的长度,只能用strlen来进行。
sizeof(str)/sizeof(str[0])
这个命令是计算str数组中所有元素的个数,并不是计算数组中有效元素的个数,一定要区分清楚!
联合体大小的计算
联合体的大小取决于:
- 其大小为所有的成员中占用空间最大的一个成员的大小(union独有的)
- 对齐方式为成员中最大的成员的数据类型长度的整数倍(和union、struct、class在对齐方式上是一样的)
看下面的代码,
#include <stdio.h>
#include <iostream>
using namespace std;
union u0
{
int i;
double j;
};
union u1
{
char a[13];
int i;
};
union u2
{
char a[13];
char b;
};
int main()
{
printf("u0:%d\n", sizeof(u0));
printf("u1:%d\n", sizeof(u1));
printf("u2:%d\n", sizeof(u2));
system("pause");
return 0;
}
先看u0,里面一个double(8),一个int(4),union的大小取决于里面所存放的最大类型,很显然,最大类型的double,那就8,再看对齐方式有没有问题,没问题,8是8的整数倍。
再看u1,里面有个char数组,是一个变量,所以,看作一个类型(大小为13),和int(4字节),那么找所存放的最大类型为13 ,再看,对齐原则,char的大小是(1字节),int的大小是4,那这个联合体的大小应该是4的整数倍,所以,(13+3)=16,凑够4的整数倍。(注意数组的特殊性,大小和最小单位,换句话说,看对齐方式的时候,char的数组的最小单位是1字节)
同理,u2,里面也是13,但最大字节数变成了char(1字节了),13是1的整数倍,所以是13
运行结果如下:
#pragma pack(x)强制修改编译器对齐方式
把上面的一个C类拿出来,按照正常的操作,C的大小应该是8,因为要对齐(C类包括:1个int(4字节)+1个short(2字节)->sizeof(C) = 4+2 = 6,因为6不是4的整数倍,所以,补2,得8;)。但我用 #pragma pack(1),强制将对齐的最小单位设置成了1,这样,类的大小,就变成了直接把类中的变量类型进行累计就行了!
#include <stdio.h>
#include <iostream>
using namespace std;
#pragma pack(1)
class C
{
public:
int i;
short j;
private:
};
int main()
{
printf("c:%d\n", sizeof(C)); //原来因为要对齐int类型,结果为8
system("pause");
return 0;
}