为什么允许多次声明typedef标识符?

时间:2014-10-07 16:03:25

标签: c typedef c99 c11

根据C99标准,6.7(5):

  

声明指定一组标识符的解释和属性。标识符的定义是该标识符的声明:   对于一个对象,导致为该对象保留存储;   对于一个功能,包括功能体;   表示枚举常量或typedef名称,是标识符的(唯一)声明。

如果带有typedef的标识符实际上是定义,那么为什么允许它们被多次声明?例如:

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

以上程序编译没有错误。这怎么可能?我期待该程序给我一个多重定义错误。

3 个答案:

答案 0 :(得分:14)

C99与C11

不同

规则在C99和C11之间变化(并且C11规则与C ++规则匹配,据我所知)。请注意,在这两个标准中,¶3在Constraints部分中,¶5在Semantics部分中。这对于错误消息很重要 - 约束违规需要诊断。

  

ISO / IEC 9899:1999§6.7声明

     

¶3如果标识符没有链接,则标识符的声明不得超过一个   (在声明符或类型说明符中)具有相同的作用域和相同的名称空间,除外   对于6.7.2.3中指定的标签。

     

5声明指定一组标识符的解释和属性。一个定义   标识符是该标识符的声明:

     
      
  • 表示对象,导致为该对象保留存储空间;
  •   
  • 用于功能,包括功能体; 98)
  •   
  • 表示枚举常量或typedef名称,是(唯一)声明的   标识符。
  •   

  

ISO / IEC 9899:2011§6.7声明

     

¶3如果标识符没有链接,则标识符的声明不得超过一个   (在声明符或类型说明符中)具有相同的作用域和相同的名称空间,除外   的是:

     
      
  • 可以重新定义typedef名称以表示与其当前相同的类型,   只要该类型不是可变修改类型;
  •   
  • 标签可以按照6.7.2.3中的规定重新申报。
  •   
     

¶5声明指定一组标识符的解释和属性。一个定义   标识符是该标识符的声明:

     
      
  • 表示对象,导致为该对象保留存储空间;
  •   
  • 用于功能,包括功能体; 119)
  •   
  • 表示枚举常量,是标识符的(唯一)声明;
  •   
  • 表示typedef名称,是标识符的第一个(或唯一)声明。
  •   

Lundin noted标准C委员会的web site包含n1360,这是一份单页文档,详细说明了此更改的原因。基本上:C ++就是这样做的;一些编译器已经做到了;它既不难做也不颠覆任何东西以允许(要求)它。

GCC并不总是强制执行标准

如果您的代码正在编译,那么它将根据C11规则或C ++规则进行编译。它不是在(严格的)C99规则下编译的。

请注意,除非您另有说明,否则GCC的最新版本允许重新定义,但请注意report John Bollinger GCC 4.4.7(在未识别的平台上)不允许重新定义全部采用C99模式。

考虑文件retypedef.c

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

使用GCC 4.9.1在Mac OS X 10.9.5上进行编译,我得到:

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

除非使用-pedantic,否则不会抱怨,只有在请求C99的情况下(符合标准,才能在C11的同一范围内重新定义typedef。)

答案 1 :(得分:2)

您的程序编译没有错误,因为您的编译器是松散的(或编译为不同的标准)。如果我使用gcc 4.4.7编译你的代码,那么它实际上确实报告了关于x的重新定义的错误。

答案 2 :(得分:1)

出于同样的原因,允许多次声明其他声明。例如:

void foo(int a);

void foo(int a);

int main()
{
    foo(42);
}

void foo(int a)
{
    printf("%d\n", a);
}

这允许多个标头声明一个函数或结构,并允许两个或多个此类标头包含在同一个翻译单元中。

typedef类似于原型化函数或结构 - 它们声明具有一些编译时间含义的标识符,但没有运行时含义。例如,以下内容无法编译:

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

因为x没有命名变量;它只是名称int的编译时别名。