【问题标题】:Why are typedef identifiers allowed to be declared multiple times?为什么允许多次声明 typedef 标识符?
【发布时间】:2014-12-02 03:18:15
【问题描述】:

来自 C99 标准,6.7(5):

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

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

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

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

【问题讨论】:

  • 啊! void main() -- 总是int main()

标签: c typedef c99 c11


【解决方案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 的编译时别名。

【讨论】:

  • 事实上,标准的引用部分确实说 typedef 声明也是一个定义,并且任何标识符只能有一个 定义。 gcc 报告 OP 代码的错误,至少使用 -Wall -pedantic -std=c99。
  • @John:啊,我想我在 C++ 领域花了太多时间。我仍然留下答案,因为正如 Jonathan Leffler 指出的那样,它在 C11 中是正确的。
【解决方案2】:

C99 与 C11 不同

C99 和 C11 之间的规则会发生变化(据我了解,C11 规则与 C++ 规则匹配)。请注意,在这两个标准中,¶3 位于约束部分,而 ¶5 位于语义部分。这对于错误消息很重要——违反约束需要诊断。

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 名称,是标识符的第一个(或唯一一个)声明。

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

GCC 并不总是执行标准

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

请注意,除非您另有坚持,否则足够新的 GCC 版本允许重新定义,但还要注意 John Bollingerreport 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 是符合标准的)。

【讨论】:

  • 谢谢。您的回答涵盖了我需要的所有细节。
  • 知道为什么这在 C11 中发生了变化吗?只是与 C++ 或其他东西协调一致?
  • @Lundin:我没有具体的原因。我认为它是“与 C++ 协调”和“它有意义且不难支持”的结合。如果您想查看C standardization committee 注释,我相信您可以找到原因——可能是查看会议记录,然后找到文档,除非有有效的搜索选项。
  • @JonathanLeffler This 是我发现的最接近基本原理的东西。如果您愿意,请随时将该链接添加到您的答案中以确保完整性:)
  • @Lundin:谢谢。见更新。我怀疑它是否有争议,因为它是一个具有现有技术和 C++ 兼容性以支持更改的单页文档(并且没有破坏现有工作代码的危险)。
【解决方案3】:

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

【讨论】:

  • 所以基本上在较旧的编译器版本中,它们遵循严格的 C99 规则。我的编译器是 GCC 4.8.1。谢谢你告诉我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-19
  • 2018-08-15
  • 2016-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多