哪些 C 规则允许在函数声明符中使用先前定义为类型的标识符?

Which C rules allow to use in function declarator an identifier, which was previously defined as a type?

考虑这段代码 (t0.c):

typedef int T;
void f(int T);

调用:

gcc -std=c11 -Wall -Wextra -pedantic -c t0.c
<nothing>

clang -std=c11 -Wall -Wextra -pedantic -c t0.c
<nothing>

cl /std:c11 /Za /c t0.c
t0.c(2): warning C4224: nonstandard extension used: formal parameter 'T' was previously defined as a type

问题:

  1. 哪些 C 规则允许在函数声明符中使用之前定义为类型的标识符?
  2. 如果 msvc 说 nonstandard extension,那么为什么符合标准的 gcc / clang 什么也没说?

1

  1. Which C rule allows to use in function declarator an identifier, which was previously defined as a type?

首先,typedef 名称 标识符。 C 2018 6.2.1 1 说:

An identifier can denote an object; a function; a tag or a member of a structure, union, or enumeration; a typedef name; a label name; a macro name; or a macro parameter…

同一段说我们可以使用相同的标识符来表示不同的实体:

… The same identifier can denote different entities at different points in the program…

第 4 段告诉我们在函数原型中声明的 T 隐藏了较早的 T:

… Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope.

2

  1. If msvc says nonstandard extension, then why standard conforming gcc / clang does not say anything?

GCC 和 Clang 什么也没说,因为代码符合 C 标准,而且它们的实现者没有选择发出任何警告。

C 标准允许 MSVC 报告超出标准要求的诊断,因为所有 C 实现都是如此,但声称它是非标准扩展是不正确的。

C17 6.2.1/2:

Different entities designated by the same identifier either have different scopes, or are in different name spaces. There are four kinds of scopes: function, file, block, and function prototype.

C17 6.2.1/4:

Every other identifier has scope determined by the placement of its declaration (in a declarator or type specifier). If the declarator or type specifier that declares the identifier appears outside of any block or list of parameters, the identifier has file scope, which terminates at the end of the translation unit.
If the declarator or type specifier that declares the identifier appears inside a block or within the list of parameter declarations in a function definition, the identifier has block scope, which terminates at the end of the associated block.
If the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has function prototype scope, which terminates at the end of the function declarator.

您的 typedef 位于文件范围内,但参数位于函数原型范围内。另见 6.2.3 关于标识符的命名空间。这些是“普通标识符”,因此内部范围始终优先于外部范围。

If msvc says nonstandard extension, then why standard conforming gcc / clang does not say anything?

据说是因为 MSVC 不好、不合规并且已知会给出错误的诊断。

您显示的代码等同于

typedef int T;
void f(int);

也许你想要

typedef int T;
void f(int T) {
    (void)T; // ignore unused parameter
}

为此 gcc ... -Wshadow ...clang ... -Weverything ... 发出诊断(gcc v6.3.0;clang v3.8.1)。