Search code examples
ctypedefc99c11

Why are typedef identifiers allowed to be declared multiple times?


From the C99 standard, 6.7(5):

A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that: for an object, causes storage to be reserved for that object; for a function, includes the function body; for an enumeration constant or typedef name, is the (only) declaration of the identifier.

If identifiers with typedef are in fact definitions, then why are they allowed to be declared more than once? Example:

int main()
{
  typedef int x;
  typedef int x;
}

Above program compiles with no errors. How is this possible? I was expecting the program to give me a multiple definition error.


Solution

  • C99 is different from C11

    The rules change between C99 and C11 (and the C11 rules match the C++ rules, as I understand it). Note that in both standards, ¶3 is in the Constraints section and ¶5 is in the Semantics section. That is important for error messages — constraint violations require a diagnostic.

    ISO/IEC 9899:1999 §6.7 Declarations

    ¶3 If an identifier has no linkage, there shall be no more than one declaration of the identifier (in a declarator or type specifier) with the same scope and in the same name space, except for tags as specified in 6.7.2.3.

    5 A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that:

    • for an object, causes storage to be reserved for that object;
    • for a function, includes the function body;98)
    • for an enumeration constant or typedef name, is the (only) declaration of the identifier.

     

    ISO/IEC 9899:2011 §6.7 Declarations

    ¶3 If an identifier has no linkage, there shall be no more than one declaration of the identifier (in a declarator or type specifier) with the same scope and in the same name space, 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;
    • tags may be redeclared as specified in 6.7.2.3.

    ¶5 A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that:

    • for an object, causes storage to be reserved for that object;
    • for a function, includes the function body;119)
    • for an enumeration constant, is the (only) declaration of the identifier;
    • for a typedef name, is the first (or only) declaration of the identifier.

    Lundin noted that the Standard C Committee's web site contains n1360, a one page document detailing why this change was made. Basically: C++ does it; some compilers already do it; it is neither hard to do nor subverting anything to permit (require) it.

    GCC doesn't always enforce the standard

    If your code is compiling, then it is being compiled under the C11 rules, or C++ rules. It is not being compiled under (strict) C99 rules.

    Note that sufficiently recent versions of GCC allow the redefinition unless you insist otherwise, but also note the report by John Bollinger that GCC 4.4.7 (on an unidentified platform) does not allow the redefinition at all in C99 mode.

    Consider the file retypedef.c:

    int main(void)
    {
        typedef int x;
        typedef int x;
        x y = 0;
        return y;
    }
    

    Compiling on Mac OS X 10.9.5 with GCC 4.9.1, I get:

    $ gcc -O3 -g -std=c11 -Wall -Wextra -Werror           -c retypedef.c
    $ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -pedantic -c retypedef.c
    $ gcc -O3 -g -std=c99 -Wall -Wextra -Werror           -c retypedef.c
    $ gcc -O3 -g -std=c99 -Wall -Wextra -Werror -pedantic -c retypedef.c
    retypedef.c: In function ‘main’:
    retypedef.c:4:17: error: redefinition of typedef ‘x’ [-Werror=pedantic]
         typedef int x;
                     ^
    retypedef.c:3:17: note: previous declaration of ‘x’ was here
         typedef int x;
                     ^
    cc1: all warnings being treated as errors
    $
    

    It doesn't complain unless -pedantic is used, and then only if C99 is requested (it is standard compliant to redefine a typedef in the same scope in C11).