STOP! Bitfields 与 对齐 align
CPU访问内 存时,总是以其整数字长为单位读写。比如 x86 CPU 总是从4字节的整数倍数地址上,读取4字节数据,它不能随心所欲地从任何位置开始读取任意长度数据。为了效率考虑,默认情况下编译器总是让整数存放于其长度的整数 倍数地址上。在一个结构中,为了做到这一点,有时不得不浪费几个字节。
举个例子,我 们定义一个结构:
代码语言:javascript复制struct {
char c;
int i;
};
从字面上看, 这个结构的长度是5个字 节,但默认情况下编译器总是分配8个字节,是为了让 i 出现在偏移量 4 地址上。
位域 bitfields 是 C 语言结构中的一个成员,可以指定该成员所占内存 的位数 bit。然而, 在位域的对齐方式上,GCC和MSVC这2个编译器产生了巨大的分歧。现在,我们将上面这个结构改成下面这样子:
代码语言:javascript复制struct {
char c;
int b: 1;
int i;
};
我们在 c 和 i 中插入了一个只占 1 位内存的整数。在 GCC 中,我们测试该结构的长度,发现仍然是 8 个字节,就是说 b 利用了 c 和 i 间的空隙,而没有多占空间。然而在 VC 中我们会发现,结构长达 12 字节。也就是说 b 像其他所有整数一样,在4倍数地址上对齐了。
如果仅仅这样 还好办,不幸的是,如果你在b后面再插入一个位域 b2,长度还是12。而如果插入一个 short 型的位域,长度将变成16!
其原因在于 VC 使用了一种古怪的对齐方式,且没有完整的文档 描述。基本上,VC 将 结构中相邻的相同数据结构位域组成位域组,然后每个位域组都默认要求按其数据类型对齐。另外还有许多不同的例外情况。这样的情况与任何一个普通 GCC 支持的 对齐模式都不同。对齐方式不同意味着什么呢?考虑一下,Windows 是用 VC 编译的,也就是说所有 Windows API 都使用 VC 对齐方式。而如果你用 MinGW GCC编译 Windows 程序,你对所有使用了位域的 Windows API 的调用都将出错!而我们的 GTK for Windows 显然也是使用了 这种对齐方式。
万幸的是,Windows 版 GCC 在编译时补上了一个新的命令行开关,-mms-bitfields,使其使用 VC 兼容的对齐方式。 Borland 所携带的这个 MinGW GCC也有。而这个开关别的平台上的 gcc 则都没有。加上这个编译开关后生成的代码将与 VC 的代码有相同的行为特征。只是 Borland 自己好像还没有意识到包里的 gcc 有着这么重要的一个开关,我找遍 Build Options Explorer 也 没有找到有这个选项的勾可打。好在此时 BCBX 的开放性再次救了我们和它自己一命。在 Options 标签页的最下面,有一个 Other options and parameters 选项。填在里面的参数将被原 封不动地拷贝到命令行上。所以我们在里面写上:
代码语言:javascript复制-mms-bitfields