结构体与类的字节对齐(终极方案,简单易懂)

先记住常用类型在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
6
typedef 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
2
3
4
5
6
struct s1{
char c; //1
int i; // 4
short f; // 2
double v; // 8
};

原本是这样:c iiii ff vvvvvvvv,从左向右按8补齐,应当是:c--- iiii ff------ vvvvvvvv。c和i总共5,给c补3位就行。f没法和v组合,只能补6位,总共24.

1
2
3
4
5
6
struct s1{
short f; //2
char c[3]; //3
int i; //4
double v; //8
};

原来是: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
9
struct 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
2
3
4
5
6
7
8
9
10
11
12
13
struct s1{
char c; //1
int i; // 4
short f; // 2
double v; // 8
};

struct s2{
char c; //1
short f; // 2
int i; // 4
double v; // 8
};

第一个的大小是24,第二个是16

#pragma pack (n)

这条预处理命令也好理解了,只要把上面规则中最大变量的大小换成n就行,注意假设结构体中最大元素占内存m,当n如果超过m是不起作用的。

类的sizeof计算

  1. 类的大小为类的非静态成员数据的类型大小之和,也就是说静态成员数据不作考虑。
  2. 普通成员函数和构造函数不影响sizeof的计算
  3. 虚函数由于虚指针的存在,所以要占据一个指针大小,也就是4字节,无论多少个虚函数
  4. 类的总大小也遵守字节对齐规则。

将类定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
class 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,实际是空类,但是空类实例化也要在内存占用地址,由编译器添加一个字节以区分不同对象。