如何为单个结构中的每个成员分配特定的偏移量?

Xfc*_*ce4 0 c struct memory-management

考虑结构:

struct MainStruct {
    struct A a;
    struct B b;
    struct C c;
};
Run Code Online (Sandbox Code Playgroud)

通常,编译器应该将 a、b 和 c 依次放入内存中并进行一定的对齐。

但是,如果我想让 a、b 和 c 的变量始终以 mainStruct 内的精确偏移量开始,而与它们的大小无关,该怎么办?“在编译时”执行此操作的便捷方法是什么?

举个例子: mainStruct 总共可能有 256 个字节,而a总是从 开始&mainStructb总是从 开始&mainstruct + 100c总是从 开始&mainStruct + 179

编辑: mainStruct 的地址可能是可变的。这里重要的一点是偏移量始终是恒定的。每当我定义类型的变量时Struct MainStruct,该变量应该始终具有相同的内存布局,并且内部结构具有恒定的偏移量。

Eri*_*hil 5

这可以通过匿名联合和编译器功能来打包结构和联合来完成。由于#pragma pack(1)似乎可以与 MSVC、GCC 和 Clang 一起使用,因此这里有一个简单的解决方案:

\n
#include <stddef.h> //  Included for offsetof macro.\n#include <stdio.h>\n\n\nstruct A { char c; int whatever; };\nstruct B { char c; int whatever; };\nstruct C { char c; int whatever; };\n\n\n#define MemberBOffset 100\n#define MemberCOffset 179\n\n#pragma pack(push, 1)\nunion MainStruct\n{\n    struct\n    {\n        //  No padding.\n        struct A a;\n    };\n    struct\n    {\n        char paddingB[MemberBOffset];\n        struct B b;\n    };\n    struct\n    {\n        char paddingC[MemberCOffset];\n        struct C c;\n    };\n};\n#pragma pack(pop)\n\n\nint main(void)\n{\n    printf("Offset of member a = %td.\\n", offsetof(union MainStruct, a));\n    printf("Offset of member b = %td.\\n", offsetof(union MainStruct, b));\n    printf("Offset of member c = %td.\\n", offsetof(union MainStruct, c));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,这通过使用更少的结构/联合嵌套和使用匿名结构改进了Henry Gilbert\xe2\x80\x99s 答案中的解决方案,以便可以以自然方式访问成员,例如foo.b代替foo.memberData.b.

\n

如果struct MainStruct需要而不是union MainStruct,则可以将联合设为匿名并嵌套在struct MainStruct. 或者,typedef可以使用 a。

\n

上面期望成员的偏移量a为零,并且其他偏移量不为零。如果情况并非总是如此,我们可以修饰:

\n
#define MemberAOffset 0\n#define MemberBOffset 100\n#define MemberCOffset 179\n\n#pragma pack(push, 1)\nunion MainStruct\n{\n    struct\n    {\n        #if 0 < MemberAOffset\n            char paddingA[MemberAOffset];\n        #endif\n        struct A a;\n    };\n    struct\n    {\n        #if 0 < MemberBOffset\n            char paddingB[MemberBOffset];\n        #endif\n        struct B b;\n    };\n    struct\n    {\n        #if 0 < MemberCOffset\n            char paddingC[MemberCOffset];\n        #endif\n        struct C c;\n    };\n};\n#pragma pack(pop)\n
Run Code Online (Sandbox Code Playgroud)\n

#pragma pack指令具有重大影响;它会影响 内部声明的任何结构union MainStruct。也就是说,如果struct A a;我们有 ,而不是struct A { char c; int i; } a;,它将打包struct A而不是使用其自然填充。在指令之前声明struct Astruct B、 和可以避免这种情况。struct C然而,GCC 和 Clang 的另一种选择是使用更精细的__attribute__表示法,而不使用#pragma pack指令。

\n
union __attribute__((__packed__)) MainStruct\n{\n    struct __attribute__((__packed__))\n    {\n        #if 0 < MemberAOffset\n            char paddingA[MemberAOffset];\n        #endif\n        struct A a;\n    };\n    struct __attribute__((__packed__))\n    {\n        #if 0 < MemberBOffset\n            char paddingB[MemberBOffset];\n        #endif\n        struct B b;\n    };\n    struct __attribute__((__packed__))\n    {\n        #if 0 < MemberCOffset\n            char paddingC[MemberCOffset];\n        #endif\n        struct C c;\n    };\n};\n
Run Code Online (Sandbox Code Playgroud)\n