我在项目中继承了一些第三方代码。它由头文件和没有源代码的二进制库组成,所以我不能用不同的设置重新编译它。
标头使用类似于编译时断言的技巧(如果表达式失败,typedef
负大小的数组)以确保正确的结构对齐,但它似乎不能在32位模式下工作。
这是一个独立的小程序,可以捕获整个问题:
#include <stdio.h>
#include <stddef.h>
#pragma pack(push, 8)
struct test {char _; long long a;};
typedef char pack_test[(offsetof(test, a) == 8) ? 1 : -1];
#pragma pack(pop)
int main(int argc, char *argv[])
{
printf("%d\n", offsetof(test, a));
return 0;
}
上面的代码无法在clang 3.0和任何最近的gcc版本上编译,我可以快速地完成(4.5到4.7):打包pragma根本没有任何效果。编译器不断将成员a
对齐到4个字节(您可以通过注释typedef
来检查它。)
为什么?如何修复代码以使此断言不会失败并使其与ABI保持兼容,同时不必遍历标头中的所有结构并将attribute
添加到其尾部?
答案 0 :(得分:3)
在32位系统上,long long
可能不需要八个字节的对齐,四个就足够了。毕竟,你仍然从内存中取出两个完整的单词,无论它们中的第一个是八字节对齐还是四个。
您可以尝试使用属性强制八字节对齐
struct test { char _; long long a __attribute__ ((aligned(8))); };
(可能是错的,我不熟悉gcc的属性)
当然,您还可以向struct
添加虚拟成员以确保所需的对齐
struct test { char _; char dummy[7]; long long a; }
这应该在实践中发挥作用。