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 Bollinger 的 report 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 是符合标准的)。