Typedef redefinition with different types ('struct TGAME' vs 'struct TGAME')

Viewed 271

I am having the compiling error of the title.

I have heard it could be because of the name I am giving to the struct, but I change it too many times to random names and that didn't fixed it.

My IDE (CLion), shows that error in PLBR.c

Here are my files:

PLBR.h

#ifndef UNTITLED2_PLBR_H
#define UNTITLED2_PLBR_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int players;
    char diff[5];
    int numpal;
    char *palabras;
}TGAME;

PLBR.c

#include "PLBR.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct{
    int players;
    char diff[5];
    int numpal;
    char *palabras;
}TGAME;

In main.c I just have the #include "PLBR.h" and some code but nothing with relation.

2 Answers

The Easy Part

This is an interesting question because answering it fully is complicated. Let’s start with the simple answer: To avoid problems, do not redefine a type in a source file. Defining it in the header file and including the header file in the source file is sufficient.

The Complicated Parts

Now for the complicated parts. You are allowed to repeat a typedef definition if it redefines a name to be the same type. C 2018 6.7 3 says:

… there shall be no more than one declaration of the identifier… except that:

— a typedef name may be redefined to denote the same type as it currently does, provided that type is not a variably modified type;…

So, if you have typedef int foo; twice or typedef char bar[3]; twice, that is fine. You can also have typedef struct MyStruct foo; twice.

However, you cannot have typedef struct { int i; } foo; twice. Even though the two declarations have identical text, they declare different types. This is because of a rule in C 2018 6.7.2.3 5:

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

So your structure is a different type each time its definition is repeated.

There is a way to refer to the same structure type more than once, and that is to give it a tag:

typedef struct MyTag {
    int players;
    char diff[5];
    int numpal;
    char *palabras;
} TGAME;

Then we can redefine TGAME without error; typedef struct MyTag TGAME; may appear, even multiple times, and it redefines TGAME to be the same type, struct MyTag, which is allowed.

However, although you can repeat the typedef, you cannot repeat the structure definition. If you have this twice:

typedef struct MyTag {
    int players;
    char diff[5];
    int numpal;
    char *palabras;
} TGAME;

the compiler will complain. This is because C 2018 6.7.2.3 says:

A specific type shall have its content defined at most once.

So, although you can repeat a typedef with the same type as many times as you like, you cannot repeat a full structure definition. To use a structure type again, you can only repeat it with the tag alone, not with the full definition of its contents.

More Complications

After all of the above, the rules about structure tags and types are still incomplete. There are still issues of scope to consider. Using struct MyTag inside a new scope can either refer to a previous type or create a new type, depending on how it is used!

Here is an example. The following gets an error because typedef TypeA TypeB; does not redefine TypeB as the same type it was first defined as:

typedef struct Tag { int x; } MyType;   // Create a new type.

void foo(void)  //  Start a new scope.
{
    typedef struct Tag TypeA;               // Define TypeA as previous type.
    typedef struct Tag { int x; } TypeB;    // Make a new type.
    typedef TypeA TypeB;                    // Error, TypeA is not same as TypeB.
}

but inserting struct Tag; changes the meaning:

typedef struct Tag { int x; } MyType;   // Create a new type.

void foo(void)  //  Start a new scope.
{
    struct Tag;                             // Declare struct Tag to be some new type.
    typedef struct Tag TypeA;               // Define TypeA as new type.
    typedef struct Tag { int x; } TypeB;    // Define new type and define TypeB to be that type.
    typedef TypeA TypeB;                    // Works, TypeA is same as TypeB.

    (void) (TypeB *) 0; // (Avoid compiler warning about unused name.)
}

The reason for this is there is a special rule for struct identifier; appearing just that way, without { … } defining members or as part of a larger declaration like a typedef. That rule is C 2018 6.7.2.3 7, which says:

A declaration of the form

struct-or-union identifier ;

specifies a structure or union type and declares the identifier as a tag of that type.

So struct Tag; says “We are declaring a new struct Tag type in this scope.” In contrast, when we have typedef struct Tag TypeA; without struct Tag;, then C 2018 6.7.2.3 9 applies. It says that if struct identifier “occurs other than as part of one of the above forms, and a declaration of the identifier as a tag is visible, then it specifies the same type as that other declaration, and does not redeclare the tag.” The “above forms” it refers to are the struct identifier; I have above and struct identifieropt { struct-declaration-list }, which defines a new type by defining its members. So struct Tag appears in typedef struct Tag TypeA; other than as one of those forms, and a prior struct Tag is visible, so this use of struct Tag specifies the same type as the previous one, not a new type.

You've already defined TGAME in PLBR.h. There's no need to redefine it in PLBR.c; doing so is not actually allowed.

One of the first stages of the C compiler is to interpret #include ... statements by substituting the contents of the referenced files. So by the time TGAME is used, the compiler sees this:

// Contents of stdio.h
// Contents of stdlib.h
// Contents of string.h

typedef struct {
    int players;
    char diff[5];
    int numpal;
    char *palabras;
}TGAME;

typedef struct{
    int players;
    char diff[5];
    int numpal;
    char *palabras;
}TGAME;
Related