canonymous-struct

Why do anonymous structs cause conflicting types


#define MyStruct(T) struct {T data;}

void foo(MyStruct(int) s);

void foo(MyStruct(int) s) {
    return;
}

int main(void) {
    //...
    return 0;
}

This results in an error:

main.c:7:6: error: conflicting types for 'foo'
void foo(MyStruct(int) s) {
     ^
main.c:5:6: note: previous declaration is here
void foo(MyStruct(int) s);
     ^

There's no error if I create a typedef, such as typedef MyStruct(int) MyIntStruct; and use that.

So my question is, why do I get the conflicting type error? Are all anonymous structs unique, like the compiler won't determine whether they're the exact same type?


Solution

  • First, these are structure declarations without tags, not anonymous structures. An anonymous structure is a structure without a name, not a structure declaration without a tag. For example:

    struct outer
    {
        struct inner { int x; int y; };
        int z;
    } s;
    

    In this code, the struct member inside s does not have a member name, so it is anonymous. We have no name that can refer to that structure, and we refer to its members as s.x and s.y, rather than s.something.x and s.something.y.

    The reason each declaration of a structure without a tag declares a distinct type is C 2018 6.7.2.3 5 says:

    … Each declaration of a structure, union, or enumerated type which does not include a tag declares a distinct type.

    A reason for this is sometimes we use structures that have identical contents for different purposes. For example, we might have a structure with two double values that we use for complex numbers (real and imaginary parts) and a structure with two double values that we use for points in a plane (x and y coordinates):

    typedef struct { double d[2]; } ComplexNumber;
    typedef struct { double d[2]; } Point;
    

    Having the compiler treat these as different types means it can give us warnings about mistakes like passing a Point as an argument where a Complex is expected.

    As you have noted, a typedef creates a new name for a type. Then using the typedef name refers to that existing type. It does not “repeat” the declaration of the struct and create a new type.