Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a redeclaration of an untagged structure a compatible type?

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 6.7.2.1 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 6.7.6.1, 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 6.7.2.3 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.

like image 423
Eric Postpischil Avatar asked Jan 25 '18 14:01

Eric Postpischil


People also ask

What do we know about the compatibility of structures?

Depending on the type of structure, there are some things that we know about its compatibility. For example, we may know that a structure may not move at certain locations in certain directions (called reactions) or we may know that a beam or column is continuous, and so its slope cannot change abruptly.

What happens if you don't give the struct tag?

After the typedef is complete, the type can then be referred to as either struct nodeor node, whichever you prefer. But if you don't give the structa tag, the type is anonymous until the typedef is complete, and cannot in any way be referred to before that.

Why is type compatibility important?

Type compatibility is important during type conversions and operations. All valid declarations in the same scope that refer to the same object or function must have compatible types. Two types are compatible if they fit any of the following categories:

What is the general form of structure declaration?

The general form of structure declaration is as follows − struct is the keyword. member1, member2 specifies the data items that makeup structure. There are three ways of declaring structure variables, which are as follows −


2 Answers

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 6.7.2.1/13,

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.

like image 78
M.M Avatar answered Oct 17 '22 02:10

M.M


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.7.2.1§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".

like image 26
Virgile Avatar answered Oct 17 '22 01:10

Virgile