您的位置:首页 > 其它

C结构体内存对齐

2016-05-24 12:41 232 查看

引言

结构体的内存对齐问题一直以来都受到很大的关注。平常使用时,我们又sizeof()操作符可以求出结构体类型的大小,也有offsetof()宏来求取结构体对应成员变量的偏移。但仅仅会使用这两者还是不够的,我们还需要掌握其成员变量在内存中的对齐方式,以在有必要的情况下,合理排布成员变量的顺序,减少结构体类型所占字节。所以今天再次走进结构体内存对齐这个话题,探索内存对齐的规则。

在下面我们先借用外力来求结构体大小以及内存对齐,然后再来分析具体细节。

sizeof()

对sizeof()要注意的一点就是是个操作符而不是函数了。相信有关操作大家也都早已清楚,那么找一个特殊的例子吧:有一个空的结构体,对它用sizeof()求大小,结果是多少?0吗?不妨在你的机子上试一下。

struct test
{};
int main()
{
int size = sizeof(struct test);
return 0;
}


当你满怀疑惑的运行起这段代码的时候,发现结果似乎有些出乎意料,不是0,而是1(准确的说是大部分人是1。这样的测试时平台相关的)。 为什么呢?

分析一下就知道了,假设我用这个结构体类型创建了两个结构体变量,struct test t1,t2;如果这个结构体类型大小为0,那么t1和t2将会拥有同一个地址,显然编译器此时无法区分这两个逻辑上不同的变量。所以主流的编译器大都为其分配一个字节的大小,用以占位,表名这个地址是被占了的,若是再分配的多一点,显得有些浪费了,没必要这么做。当然还有少数编译器, 它们为其分配的字节数可能会多一点,这是编译器决定的。

offsetof()

这是一个宏,在stddef.h头文件中。用来求取结构体中成员变量的偏移,用法也很简单(如下求 i 的偏移):

struct test
{
char c;
int i;
};
int main()
{
int offset = offsetof(struct test, i);
return 0;
}


这个例子运行后offset的值为4.

库函数中的offsetof实现方法:

#define offsetof(s,m)   (size_t)&(((s *)0)->m)


结构体内存对齐规则

1、第一个成员在与结构体变量偏移量为0的地址处。

2、其它成员变量要对齐到某个数(对齐数)的整数倍的地址处。

对齐数:编译器的 默认对齐数 与 该成员大小 中的较小值(对齐数不是默认对齐数)。

VS中默认对齐数为 8

linux中默认对齐数为4

3、结构体总大小为 最大对齐数 的整数倍。

4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数(结构体中的成员的最大对齐数)的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

举个栗子(在VS中):

struct D
{
double b;
char c;
int i;
};


b的偏移为0,因为是第一个变量。b的默认对齐数为8,b的大小为8,对齐数(前两者中较小值)为8;

c的默认对齐数为8,c的大小为1,对齐数为1,偏移为8;

i的默认对齐数为8,i的大小为4,对齐数为4,偏移为12(对齐到对齐数的整数倍处)。

计算大小为16字节,然后整体大小要为最大对齐数(8,1,4中的最大值)的整数倍,已经满足,无需补齐。如下图所示:



特殊例子(注意下面这个结构体B并没有定义一个结构体变量):

struct A
{
struct B
{
char a;
};//在C语言中相当于没有外边的大括号,sizeof(struct A)==2;在C++中相当于整个B都没有,sizeof(struct A)==1
char b;
};


修改默认对齐数

默认对齐数也是可以修改的:

#pragrma pack (4) //修改默认对齐数为4

//code

#pragram pack () //之间默认对齐数为4
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: