关于VC,GCC成员变量的内存对齐和偏移量的问题

VC中是这样定义的

注意添加头文件#include <stddef.h>

/* Define offsetof macro */
#ifdef __cplusplus

#ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&reinterpret_cast<const volatile char&>((((s *)0)->m)) )
#else
#define offsetof(s,m)   (size_t)&reinterpret_cast<const volatile char&>((((s *)0)->m))
#endif

#else

#ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

#endif /* __cplusplus */


稍后推出内存对齐的内容:


VC下:

我记得没错的话是:

struct tt{

    char p;

    double a;

    char b;

    short c;

    char d[5];

    char e[4];

}tmp;

1:结构体变量的首地址能够被其最宽基本类型成员的大小所整除。

这里的所谓的最宽基本类型成员的意思不是说这个成员占内存的大小,比如char a[7]的最宽基本类型成员的大小为1,int a的最宽基本类型成员大小为4,而稍微复杂的结构体则同样以结构体里的最宽基本类型成员的大小为准,例如下面的结构体,在VC下是8,在GCC下是4(为什么是4的原因后面解释)

struct {

    int a;

    char b;

    double c;

}

 

3:找每一个成员变量起始位置,首先改起始位置相对于结构体首地址的偏移量(offset)必须是成员变量的模数大小的整数倍,比如

这里的成员p,从0开始,占位1,分别为,0

这里的成员a,从1开始,占位8,由于必须是double最大位宽(8)的倍数,所以调整到8,所以占内存位置为8,9,10,11,12,13,14,15

这里的成员b,偏移量接在double a的后面,第16个位置,16是char 的整数倍,所以b就放在16的位置,占内存位为16

这里的成员c,偏移量从17开始,但是17不是shart的整数倍,所以填充空字符重新调整到18的位置,所以c放在18的偏移地址上,占2个字符位,分别为18,19

这里的成员d,偏移位置从20开始,是char的整数倍,所以直接开始写数据,占据5个字符位,分别是20,21,22,23,24,

这里的成员e,偏移位置从25开始,是char的整数倍,所以直接开始写数据,占据4个字符位,分别是25,26,27,28,

综上所述,总共占内存29个(0到28), 

3:再然后查找最大的内存对齐模数,这里的结构体中是8

然后要求最后的大小必须是8的倍数,即8 16 24 32等

由于这条规则,需要是最大内存对齐模数的整数倍,也就是32,所以最后的sizeof(tt)即为32。

 

当然我上面说的全是VC下的对齐结果。

 

接下来我说下GCC的对齐结果,请大家不要仅仅关注最后的大小,而应该明确知道各个成员变量的内存分布。

GCC下:

基本上和上面的步骤相似,但不同的地方是

而在GNU GCC编译器中,遵循的准则有些区别,对齐模数不是像上面所述的那样,根据最宽的基本数据类型来定。在GCC中,对齐模数的准则是:对齐模数最大只能是 4,也就是说,即使结构体中有double类型,对齐模数还是4,所以对齐模数只能是1,2,4。而且在上述的三条中,第2条里,offset必须是成员大小的整数倍,如果这个成员大小小于等于4则按照上述准则进行,但是如果大于4了,则结构体每个成员相对于结构体首地址的偏移量(offset)只能按照是4的整数倍来进行判断是否添加填充。

所以这里的步骤是这样的:

这里的成员p,从0开始,占位1,分别为,0

这里的成员a,从1开始,占位8,由于double的位宽>4,由于gcc的准则原因,对齐模数调整到4,所以占位调整到4,所以占内存位置为4,5,6,7,8,9,10,11

这里的成员b,偏移量接在double a的后面,第12个位置,12是char 的整数倍,所以b就放在12的位置,占内存位为12

这里的成员c,偏移量从13开始,但是13不是shart的整数倍,所以填充空字符重新调整到14的位置,所以c放在14的偏移地址上,占2个字符位,分别为14,15

这里的成员d,偏移位置从16开始,是char的整数倍,所以直接开始写数据,占据5个字符位,分别是16,17,18,19,20,

这里的成员e,偏移位置从21开始,是char的整数倍,所以直接开始写数据,占据4个字符位,分别是21,22,23,24

综上所述,总共占内存25个(0到24), 

然后要求最大的内存需要时对齐模数的整数倍,而由于double的存在,导致最大内存模数为4,所以最后的sizeof(tt)即为28。

 

 

通过例子应该明白很多了。这样应该能看明白了吧。。。


你可能感兴趣的:(c,struct,gcc,编译器)