Is struct packing deterministic?

后端 未结 8 2075
北荒
北荒 2020-12-13 18:00

For example, say I have two equivalent structs a and b in different projects:

typedef struct _a
{
    int a;
    double b;
    char         


        
8条回答
  •  爱一瞬间的悲伤
    2020-12-13 18:23

    ISO C says that two struct types in different translation units are compatible if they have the same tag and members. More precisely, here is the exact text from the C99 standard:

    6.2.7 Compatible type and composite type

    Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.5 for declarators. Moreover, two structure, union, or enumerated types declared in separate translation units are compatible if their tags and members satisfy the following requirements: If one is declared with a tag, the other shall be declared with the same tag. If both are complete types, then the following additional requirements apply: there shall be a one-to-one correspondence between their members such that each pair of corresponding members are declared with compatible types, and such that if one member of a corresponding pair is declared with a name, the other member is declared with the same name. For two structures, corresponding members shall be declared in the same order. For two structures or unions, corresponding bit-fields shall have the same widths. For two enumerations, corresponding members shall have the same values.

    It seems very strange if we interpret it from the point of view of, "what, the tag or member names could affect padding?" But basically the rules are simply as strict as they can possibly be while allowing the common case: multiple translation units sharing the exact text of a struct declaration via a header file. If programs follow looser rules, they aren't wrong; they are just not relying on requirements for behavior from the standard, but from elsewhere.

    In your example, you are running afoul of the language rules, by having only structural equivalence, but not equivalent tag and member names. In practice, this is not actually enforced; struct types with different tags and member names in different translation units are de facto physically compatible anyway. All sorts of technology depends on this, such as bindings from non-C languages to C libraries.

    If both your projects are in C (or C++), it would probably be worth the effort to try to put the definition into a common header.

    It's also a good idea to put in some defense against versioning issues, such as a size field:

    // Widely shared definition between projects affecting interop!
    // Do not change any of the members.
    // Add new ones only at the end!
    typedef struct a
    {
        size_t size; // of whole structure
        int a;
        double b;
        char c;
    } a;
    

    The idea is that whoever constructs an instance of a must initialize the size field to sizeof (a). Then when the object is passed to another software component (perhaps from the other project), it can check the size against its sizeof (a). If the size field is smaller, then it knows that the software which constructed a is using an old declaration with fewer members. Therefore, the nonexistent members must not be accessed.

提交回复
热议问题