(转) 从D3DXMATRIX和D3DXMATRIXA16来说对齐方式

这篇文章把人都看晕了。

 

D3D本身定义了矩阵类型,最基本的是 D3DMATRIX:
typedef struct _D3DMATRIX {
union {
struct {
float        _11, _12, _13, _14;
float        _21, _22, _23, _24;
float        _31, _32, _33, _34;
float        _41, _42, _43, _44;

};
float m[4][4];
};
} D3DMATRIX;

然后D3DXMATRIX继承了D3DMATRIX,并扩展定义了矩阵的运算符

然后D3DXMATRIXA16 定义为:

typedef __declspec( align(16) )  D3DXMATRIXA16    D3DXMATRIXA16

所以,D3DXMATRIXA16和D3DXMATRIX的区别在于前者的内存起始地址总是16的整数倍;
而后者的内存起始地址为4的整数倍; 用D3DXMATRIXA16的好处是:引用官方的说明——
16-byte aligned matrix, when used by D3DX math functions, has been optimized for improved performance on Intel Pentium 4 processors.
When using vectors and quaternions with D3DX math functions, use _declspec(align(16)) to generate byte aligned vectors and quaternions, because they will perform significantly better.

注意D3DXMATRIXA16 和 D3DXMATRIX 的sizeof()都是64字节;

下面来谈谈 内存对齐 到底是怎么回事:
对于结构体

struct  MyStruct

double dda1;
char     dda;
int        type;
};
double的sizeof()是8,char是1, int 是4, 而然在VC中sizeof( MyStruct) 的大小 != (13)8+1+4, 而是为16;为什么呢?首先必须知道:  
1.默认情况下,各(简单类型的)成员的变量存放的起始地址相对结构体起始地址的偏移量必须为该类型所占字节数(可以理解为该类型的 字节边界数,见后面说明)的整数倍:
double类型的起始地址 必须为 8的倍数;
char类型的起始地址必须为1的倍数;
int类型的起始地址必须为4的倍数;
所以,dda1的起始(偏移)地址0,满足,dda的起始地址为8,满足, type的起始地址为9,不满足,所以要填充3位,之后type起始地址为12,满足,此时占有空间8+1+3+4
2.默认情况下,会保证结构体大小 为 结构体的字节边界数 的整数倍, 所谓字节边界数就是 该结构体中占用最大空间的类型所占用的字节数;
对于上面的例子,double类型的8字节是MyStruct的字节边界数,所以结构体大小必须是8的倍数,这里
8+1+3+4为16,满足;当改变结构体顺序后:
struct MyStruct2

char      dda;
double  dda1;
int        type;
};
此时结构体占有空间为1+7+8+4+4=24;其中7比特是第一条规则的填充,最后一个4比特是第二条规则的填充;

下面再看一个有趣的例子:
struct S1

char    a;
int       b;


struct  S2

char         c;
struct S1  d;
double     e;
};

问结构体S2的大小是多少呢?  
结构体S1的大小,由上面说的可以知道为8,其字节边界数为4;
在S2中有成员d,而d不是简单类型,而是结构体,它的默认对齐方式是 其字节边界数,这里为4;
所以,在S2中,为a分配1,然后填充3,为S1类型的d分配8(1+3+4),此事偏移地址为12,所以还要填充4到16,再为double的e分配8,共计24,是8的倍数;

所以,说去说来,主要就是3点,总结如下:
1结构体在内存中的首地址能被其中最宽基本类型成员的大小所整除;
2 各成员的在结构体内部的起始偏移地址为每个成员字节边界数的整数倍, 
3 结构体总大小为 max(字节边界数)的整数倍;


上面只是默认情况,我们可以通过两个命令来修改默认规则,他们是:
__declspec( align( n ) )  和 #pragma pack(n );


#pragma pack(n )用来改变默认的对齐系数,在VC里面默认为8,gcc里面默认为4;
下面来介绍 一个概念 ——  对齐系数
当对齐系数设置为n时, 变量的 字节边界数为  min(n, 本来的字节边界输);
举例:
#pragma pack(push)
#pragma pack( 4 )
struct MyStruct

char     dda;
double dda1;
int        type;
};
#pragma pack(pop)

这里变量x的记字节边界数 为 E(x):
如果没有pack(4),E(dda) = 1;E(dda1) = 8; E(type) = 4 ; E(MyStruct)= E(dda1)= 8;
所以分配大小 1+7+8+4+4 = 24;

加上pack(4)以后,E(dda) = 1; E (dda1) = 4, E(type) = 4; E(MyStruct ) = 4;
所以此时分配的内存大小为 1+3+8+4+0 = 16


__declspec(align(n)) 则规定了 字节边界数的最小值
__declspec(align(16)) struct STRB
{
float a;
};

没有__declspec(align(n)),STRB的大小为1;
加了__declspec(align(n)),STRB结构体的sizeof为16;
注 1__declspec(align(n))的作用对象是结构体,对基本类型不起作用;
2__declspec(align(n))的优先级比#pragma pack(n)的优先级高

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值