关于内存对齐
事情的起因是这样的,我想在GNova写一个标识日期时间的类GDateTime,存储以下数据:
年份、月份、日期、星期、一年中的第几天、时、分钟、秒、毫秒。
并且可以针对以上数据进行操作。出于节省数据空间的考虑,每一项数据都尽量用最小容量的数据类型,比如年份使用guint16来存储,月份使用guint8来存储。这样以上所有的数据加在一起只占据了12个字节的数据空间。为了以后序列化的方便,我将所有的数据存储在一个字节数组中。
综合以上,我使用以下的一个数据结构来存储日期:
gbyte m_tDateTime[G_DATE_TIME_SIZE];
其中宏G_DATE_TIME_SIZE定义为一个值为12的整型字面值,这是一个能够容纳以上所有数据的最小尺寸。
当需要做日期的拷贝时,可以使用内存拷贝操作:
GMemCopy(m_tDateTime, dt.m_tDateTime, G_DATE_TIME_SIZE);
实际情况中,在执行以上代码时,系统会崩溃(vs2015):
Run-Time Check Failure #2 - Stack around the variable 'dt' was corrupted.
这是因为m_tDateTime这个字段的默认对齐规则是4个字节,而12这个数字,不满足对齐规则。在进行内存拷贝的时候,数组将以4个字节为单位进行拷贝,这样会造成内存越界。将12改为16,bug就消失了。
在C++11中,也可以通过alignas去设置对齐方式:
alignas(16) gbyte m_tDateTime[G_DATE_TIME_SIZE];
如上,由于改变了成员变量的对齐方式为16个字节,哪怕不把G_DATE_TIME_SIZE的值由12改成16,也能够正常运行,但此时也会带来存储空间的变化。
为了尽量减少存储空间的同时,还需要满足数据对齐方式,我们可以手工修改存储时的偏移量,最终修改情况如下:
共占据16个字节,其中有4个字节是用于内存对齐的无效数据,空间占据情况如下:
| 地址偏移(单位:字节) | 说明 |
| 0 | 存储年份,占2个字节 |
| 2 | 存储月份,占1个字节 |
| 3 | 存储日期,占1个字节 |
| 4 | 存储星期,占1个字节 |
| 5 | 存储一年中的第几天,占2个字节 |
| 7 | 用于内存对齐的无效数据,占1个字节 |
| 8 | 存储小时,占1个字节 |
| 9 | 存储分钟,占1个字节 |
| 10 | 存储秒数,占1个字节 |
| 11 | 用于内存对齐的无效数据,占1个字节 |
| 12 | 存储毫秒数,占2个字节 |
| 14 | 用于内存对齐的无效数据,占2个字节 |
这样子设置存储规则,就可以使用原本值为4的内存对齐方式,而且为了保证获取数据时,不会因为内存对齐方式导致数据只能部分获取,在秒与毫秒之间,插入一个字节的无效数据。
在此基础上,GNova中的日期时间类GDateTime也能够正常、高效的运行了。
浙公网安备 33010602011771号