您的位置:首页 > 编程语言 > C语言/C++

C/C++结构体和类中的内存对齐

2015-09-17 14:51 197 查看
在对结构体或者类进行 sizeof 的时候,其涉及到内存对齐的相关规则。了解内存对齐的规则,我们便可以通过指针去访问类或结构体中的成员(对于我们理解结构体和类的内存布局以及指针的用法有极大好处)。

先来看一个例子:

typedef struct {
int a;
double c;
short b;
}S_A;

typedef struct {
int a;
short b;
double c;
}S_B;


sizeof(S_A) = ?   sizeof(S_B) = ? 想一想这个问题?

答案是sizeof(S_A) = 24,   sizeof(S_B) = 16。这是非常简单的一个例子,体现了结构体的内存对齐规则。

在结构体中,从结构体的首地址开始,定义偏移量0。

对于结构体S_B,a的偏移量为0,占4个字节(0 - 3),b的偏移量为4,占2个字节(4 - 5),c的偏移量为8,占8个字节(8 - 15)。

对于结构体S_A,a的偏移量为0,占4个字节(0 - 3),c的偏移量为8,占8个字节(8 - 15),b的偏移量为16,占2个字节(16 - 17)。

这就是内存对齐,对齐规则是按照成员的声明顺序,依次安排内存,其偏移量为成员大小的整数倍,0看做任何成员的整数倍,最后结构体的大小为最大成员的整数倍(所以这里的S_A的大小是24,而不是18)。

另外对于一些特殊的成员,比如结构体嵌套或者数组,看下面例子。

#include <stdio.h>
#include <iostream>

#define OBJOFF(obj,member) (((char*)&obj.member) - ((char*)&obj))

typedef struct {
char d;
int c;
int a;
}S_A;

typedef struct
{
char x;
S_A sa;
int i;
}S_B;

int main(void)
{
std::cout << sizeof(S_B) << std::endl;

S_B s;
std::cout << OBJOFF(s, x) << std::endl;
std::cout << OBJOFF(s, i) << std::endl;
std::cout << OBJOFF(s, sa) << std::endl;

return 0;
}




输出sizeof(S_B) = 20。 对于结构体中嵌套了其他结构体时在进行内存对齐的时候,嵌套结构体的对齐值为嵌套结构体中最大成员(这里的int c)的整数倍,意思是成员结构体的开始(这里的char d)偏移量是嵌套结构体最大成员(这里的int c)大小的整数倍。

这里有个对齐值的概念,一般都有一个默认的对齐值8,对齐值可以使用#pragram pack (X)   将对齐值修改为X,一般进行对齐的时候会使用MIN(数据成员大小,默认对齐值)进行对齐。对于嵌套结构体,嵌套结构体进行对齐的时候,首个嵌套结构体的成员将按照嵌套结构体的对齐值(MIN(数据成员大小,默认对齐值))进行对齐。

S_B的内存布局如下:



对于包含数组的情况,如果是基本数据类型的话,就相当于声明了多个连续的相同类型的成员,如果是结构体数组的话,也可以看做连续声明的成员,不同的是结构体成员与其他成员是隔离开来的(见上一段文字)。例子如下:

typedef struct {
char d;
double c;
int a;
}S_A;

typedef struct
{
char x;
S_A sa[2];
int i;
}S_B;
其中S_B的内存布局应该是这样的:



接下来再来说说类中的内存对齐。

类的内存对齐和结构体的内存对齐类似,不过需要注意的是,类中如果出现静态成员变量,静态成员变量的大小是不会计入到类的大小中的,普通函数也不会对类大小有影响。如果是虚函数的话,在类的内存中首先会存虚函数表的指针,这些知识可以去找找其他资料,推荐去看看陈皓的C++ 虚函数表解析,写得非常详细。

我是一名刚进入大三的学生,文笔也不太行。写的不太清楚的地方请见谅,如有错误,欢迎指正。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C C++ 内存对齐