先记住常用类型在32和64位的字节
类型 | 32位 | 64位 |
---|---|---|
char | 1 | 1 |
int | 4 | 4 |
short | 2 | 2 |
float | 4 | 4 |
double | 8 | 8 |
指针 | 4 | 8 |
只有指针在64位时不同,是8。函数指针的typedef声明不参与计算。枚举类型占内存4字节。
另外注意:gcc中没有要求结构体大小是最大对象的整数倍。
字节对齐
终于搞清楚结构体的字节对齐怎么计算了,看了那么多国内博客,大部分都不靠谱,要不然就是不知所云,最后看了一个印度三哥的视频讲解,没用3分钟就明白了。其实就一条规则:计算时按最大成员的大小进行逐个判断,有需要就补位
直接看几个例子:1
2
3
4
5
6typedef struct bb
{
int i; //4
double w; //8
float h; //4
};
我们以为它在内存中是这样的: iiii wwwwwwww hhhh
其实是这样的:iiii ---- wwwwwwww hhhh ----
最大的double占8个,从左向右,每8个为一组,编译器无法把iiii wwww
一起处理,那样就把double截断了,所以给int补4位。同理float补4位。总共24.
1 | struct s1{ |
原本是这样:c iiii ff vvvvvvvv
,从左向右按8补齐,应当是:c--- iiii ff------ vvvvvvvv
。c和i总共5,给c补3位就行。f没法和v组合,只能补6位,总共24.
1 | struct s1{ |
原来是:ff ccc iiii vvvvvvvv
,2+3不足8,2+3+4超过了8,所以给f补3位,然后i补4位,也就是2+3+3+4+4+8=24
最后来个特殊的,计算N的大小:1
2
3
4
5
6
7
8
9struct Node{
char c;
int i;
char p;
};
struct N{
Node n; //12
int x; //4
};
按上面的方法可知Node占12,那么按上面的方法,N是不是该占24?错了,在N里的Node应该按cccc iiii pppp
处理,这样N就占16.
1 | struct s1{ |
第一个的大小是24,第二个是16
#pragma pack (n)
这条预处理命令也好理解了,只要把上面规则中最大变量的大小换成n就行,注意假设结构体中最大元素占内存m,当n如果超过m是不起作用的。
类的sizeof计算
- 类的大小为类的非静态成员数据的类型大小之和,也就是说静态成员数据不作考虑。
- 普通成员函数和构造函数不影响sizeof的计算
- 虚函数由于虚指针的存在,所以要占据一个指针大小,也就是4字节,无论多少个虚函数
- 类的总大小也遵守字节对齐规则。
将类定义如下:1
2
3
4
5
6
7
8
9
10
11
12class Base
{
public:
Base();
explicit Base(int a);
virtual ~Base();
void test();
virtual void test_virtual();
private:
void foo();
protected:
int m;
执行sizeof的结果是8,如果把虚析构函数去掉就变成4,再把int m
去掉就变成了1,实际是空类,但是空类实例化也要在内存占用地址,由编译器添加一个字节以区分不同对象。