Is a redeclaration of an untagged structure a compatible type?

十年热恋 提交于 2021-02-20 06:12:43


For purposes expressed in this question, we want to do this:

typedef struct { int a; } A;
typedef struct { struct { int a; }; int b; } B;

A *BToA(B *b) { return (A *) b; }
B *AToB(A *a) { return (B *) a; }

The desire is that the casts conform to C 2011 15, which says “A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa.”

Since the struct { int a; } inside B does not have a name, let’s call it A'.

“Suitably” is not explicitly defined. I presume that if A * is a valid pointer to an object of type A', then (A *) b performs a suitable conversion, and, similarly, if a is a pointer to an A' that is in a B, then (B *) a is a suitable conversion.

So the question is: Is A * a valid pointer to an object of type A'?

Per, A * is compatible with A' * if A is compatible with A'.

Per 6.2.7, “Two types have compatible type if their types are the same… 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 completed anywhere within their respective translation units, 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; if one member of the pair is declared with an alignment specifier, the other is declared with an equivalent alignment specifier; and if one member of the pair is declared with a name, the other is declared with the same name. For two structures, corresponding members shall be declared in the same order…”

These cannot be the same type by 5: “Each declaration of a structure, union, or enumerated type which does not include a tag declares a distinct type.”

Since they are not the same type, are they compatible? The text in 6.2.7 says they are compatible if declared in separate translation units, but these are in the same translation unit.


As you laid out in the question, the standard clearly and unambiguously says that two struct definitions struct { int a; } in the same translation unit declare two incompatible types. Notwithstanding the fact that this might be "weird". Compilers have always followed the standard.

This seems like reasonable behaviour to me: if you happen to have semantically unrelated structs in your project that coincidentally have a member list with the same types, you do want the compiler to reject an accidental assignment between the two.

Re. the code in your question, according to,

The members of an anonymous structure or union are considered to be members of the containing structure or union.

So I would treat the definition of B as being equivalent to:

typedef struct { int a; int b; } B;

for purposes of further analysis.


I haven't seen anything in the standard that says that both struct are compatible and thus I would say that they are not.

The only thing that could get you a limited compatibility between the structures is the use of an union, as mentioned in§6:

One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the completed type of the union is visible.

i.e., something like

typedef struct { int a; } A;
typedef struct { union { struct { int a; }; A export; }; int b; } B;

A *BToA(B *b) { return &b->export; }
B *AToB(A *a) { return (B *) a; }

should be safe, but for read accesses only: the standard did not really bother to specify what "inspecting" the common initial sequence means, but seems to use it in opposition to "modify".


There are two situations in which compatibility of structures is relevant:

  1. In deciding whether values or pointers of one type may be coerced into values or pointers of the other, without use of the casting operator and without producing a diagnostic. Note that for this purpose, separately-declared structures are incompatible even if they are structurally identical, but that this kind of compatibility is irrelevant when passing structures or pointers between compilation units.

  2. In deciding whether a value of, or pointer to one type, may be safely given to code which expects the other. Passing untagged structures between compilation units would be impossible if structurally-identical types were not regarded as compatible for this purpose. Compilers used to regard structurally-identical types as compatible for this purpose even within a compilation unit, and there was never any good reason for compilers to do otherwise in cases where one or both types are untagged, but because the Standard doesn't mandate such treatment it has become fashionable for compilers to senselessly weaken the language by blithely assuming that a pointer to one such type won't be used to access members of another.

Unfortunately, when the Standard was written, its authors didn't think it was important to expressly mandate all the obviously-useful things that compilers were already doing, and which sensible compilers would continue to do. The net result is that useful constructs which used to be supported and non-controversial will be unreliable unless otherwise-useful optimizations are disabled.