5

The C standard states:

A pointer to a structure object, suitably cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa.

Is there any possible (and well-defined) way to perform such a "suitable cast" in C11 if the first member of the struct in question is an anonymous struct/union? Or to perform the "vice versa" backward cast if the containing struct is anonymous?

I guess that casting to a non-anonymous struct with the same member sequence as the anonymous struct would make this not well-defined as they are not compatible, and, therefore, not guaranteed to have the same memory layout.

However, the C standard states:

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 <...>

Can we try to apply this rule to anonymous structs? Say, if we have the following setup:

header.h:

struct container {
    struct {
        int a;
        char b;
    };
};

void print(struct container *pcontainer);

sep.c:

#include <stdio.h>
#include "header.h"

void print(struct container *pcontainer){
    printf("%d\n", ((struct { int a; char b; }*)pcontainer)->a);
}

main.c:

#include "header.h"

int main(void){
    struct container container, *pcontainer;

    pcontainer = &container;
    pcontainer->a = 1;

    print(pcontainer);

    return 0;
}

(this compiles on gcc (GCC) 4.8.3 20140911 and outputs 1).

Consider the anonymous struct used in the cast inside the print function and the anonymous struct that is the first member of the struct container that winds up in main.c. Could they be considered "types declared in separate translation units"? Also, do they really satisfy all the other compatibility requirements, or am I misunderstanding something?

  • 1
    As a note, identical structures always have the same memory layout (at least for an implementation where separate source files can be compiled and linked independently), even if they are incompatible, because of the possibility that there is another identical structure compatible with both of them in another translation unit. The question here is if strict aliasing is violated. – mafso Jan 13 '15 at 17:30
  • @mafso: of course, I am well aware of that. I am simply interested if the behaviour in this case is well-defined by the standard. –  Jan 13 '15 at 17:35
  • As another note, this isn't really about anonymous structs, but about structures without a tag. So the question also applies to C99. – mafso Jan 13 '15 at 17:36
  • 1
    I referred to _[...] not well-defined as they are not compatible, and, therefore, not guaranteed to have the same memory layout._ An implementation, where they don't, would be quite contrived. Strict-aliasing, on the other hand, may become an issue with real code on a real implementation. (And I'm also curious about the question, I didn't question it.) – mafso Jan 13 '15 at 17:40

1 Answers1

2

What is a translation unit:

5.1.1.1 Program Structure

  1. A C program need not all be translated at the same time. The text of the program is kept in units called source files, (or preprocessing files) in this International Standard. A source file together with all the headers and source files included via the preprocessing directive #include is known as a preprocessing translation unit. After preprocessing, a preprocessing translation unit is called a translation unit.

So the c file plus the headers after preprocessing form a single translation unit. Let's take the translation unit which is made out of sep.c and header.h. It contains two declarations of the struct struct { int a; char b; }, one in the struct container and the other in the function print. Those structs are declared in the same translation unit.

6.2.7 Compatible type and composite type

  1. 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.6 for declarators. Moreover, two structure, union, or enumerated types declared in separate translation...

the remaining text is referring to types declared in separate translation units.

Since the structs are not declared in separate translation units, they do not fall under the 6.2.7 rule.

Therefore in my interpretation the structs, the one in struct container and the other in the cast in the print(), aren't compatible.

Community
  • 1
  • 1
2501
  • 25,460
  • 4
  • 47
  • 87
  • Yes, but the question was about the `struct container` that winds up in `main.c`. Technically, if I understand correctly, it's supposed to be the real type of the object pointed to by the pointer passed to the print function. Of course it is compatible with the `struct container` declared in `sep.c`, but wouldn't this make it a separate concern? –  Jan 13 '15 at 16:52
  • @Mints97 Where the pointer comes from doesn't matter. Calling the function from main doesn't change the fact that the structs are not compatible in the other translation unit. – 2501 Jan 13 '15 at 16:57
  • yes, the two anonymous structs declared in `sep.c` are not compatible, but, according to the standard, the `struct container`s in the two .c files are two different structs. Do you mean to say that passing our structure pointer to `print` changes the effective type of the pointed-to variable? If yes, why? –  Jan 13 '15 at 17:02
  • 1
    @Mints97 No, type struct container in main is compatible with the one in sep, and that is what you pass. But the anonymous structs aren't. ( This is my interpretation. Interesting question though. ) – 2501 Jan 13 '15 at 17:03
  • 1
    thanks for your input, let's wait for someone else's interpretation of the standard. You didn't convince me =P –  Jan 13 '15 at 17:07
  • 2
    @Mints97: That's an interesting objection. The two structures in sep.h are clearly incompatible (as shown in this answer), although they both appear to be compatible with the structure in main.c. I always found the fact that the "compatible type" relation isn't transitive a little unintuitive, but I've never thought of an example like this one. – mafso Jan 13 '15 at 17:19
  • @mafso: It makes sense for "compatible type" to be non-transitive, though gcc's aliasing behavior is IMHO based on a horrible misinterpretation of the Standard's intent. The One Program Rule recognizes that it's possible for an implementation to be simultaneously fully compliant and yet totally useless; the fact that the Standard doesn't explicitly mandate recognition of certain type-punning patterns where aliasing is obvious doesn't mean that failure to recognize such patterns will not seriously and needlessly degrade the usability of the compiler for many purposes. – supercat Jul 14 '16 at 21:22
  • @mafso: An example of a situation where non-transitive behavior would make sense would be if struct types `s1` and `s2` share common initial members with `header`, and there exists a union type which combines `s1` and `header`, as well as one that combines `s2` and `header`. The existence of such types should make it possible to have a function that accepts pointers of type `header` use the common fields of types `s1` or `s2`, even though `s1` and `s2` would not be alias-compatible with each other. – supercat Jul 14 '16 at 21:24