C中的冲突类型和变量命名

per*_*ror 11 c gcc clang

我最近偶然发现了一个奇怪的代码构造,它导致C编译器进入一个奇怪的状态.我想解释一下为什么会这样.

这是我的小代码片段,演示了这个问题:

#include <stdlib.h>

typedef int type_t;

int main (void)
{
  int a = 10, b = 100;

  type_t *type_t = &a; // We name a variable with type name
  type_t *type_c = &b; // We define a variable with type_t

  return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

这是显示的错误消息gcc:

#> gcc -Wall -Wextra -o sample sample.c 
sample.c: In function ‘main’:
sample.c:11:11: error: ‘type_c’ undeclared (first use in this function); did you mean ‘type_t’?
   type_t *type_c = &b;
           ^~~~~~
           type_t
sample.c:11:11: note: each undeclared identifier is reported only once for each function it appears in
Run Code Online (Sandbox Code Playgroud)

或者,用clang:

#> clang -Wall -Wextra -o sample sample.c 
sample.c:11:11: error: use of undeclared identifier 'type_c'; did you mean 'type_t'?
  type_t *type_c = &b;
          ^~~~~~
          type_t
sample.c:10:11: note: 'type_t' declared here
  type_t *type_t = &a;
          ^
sample.c:11:10: error: invalid operands to binary expression ('type_t *' (aka 'int *') and 'type_t *')
  type_t *type_c = &b;
  ~~~~~~ ^~~~~~~
2 errors generated.
Run Code Online (Sandbox Code Playgroud)

请注意,如果我们修改代码如下,它编译正常:

int main (void)
{
  int a = 10, b = 100;

  type_t *type_c = &b; // We define a variable with type_t
  type_t *type_t = &a; // We name a variable with type name

  return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

那么,我的问题吧!

似乎错误是由于赋值运算符'='的l值被误认为是type_t和之间的乘法而产生的type_c.如type_c未知,它解释了错误消息.

但是,为什么我们对l值有这种混淆?type_t参考类型而不是变量不应该是明确的吗?

我想这是不是一个实现的问题,因为这两个,gccclang相同的行为.但是,我真的很想知道这个问题的关键.

Jon*_*ler 11

这是预期和正确的行为.在C中,有许多名称空间:标签,成员,标签和普通标识符.一个typedef名称是普通的标识符名字空间,因为是变量名.

你有:

type_t *type_t = &a; // We name a variable with type name
type_t *type_c = &b; // We define a variable with type_t
Run Code Online (Sandbox Code Playgroud)

这里有三次type_t.第一个是typedef名称; 没问题.第二个是新的变量名称; 没问题,但它隐藏(阴影)typedef名称; 您不能再引用type_t当前代码块中的类型.第三种情况是指变量; 您将整数指针乘以未定义的变量(并尝试将结果用作l值来接收地址b),这在许多级别上都是错误的.

当你颠倒这些行的顺序时,它工作正常,因为type_c声明是type_t *正常的; 然后type_t定义为类型的变量type_t *,但不能type_t对当前块中的类型进行进一步的引用(任何此类引用都是变量,而不是类型).

请注意,如果它typedef位于函数内部,则无法使用它进行遮罩type_t *type_t = &a;.见C11§6.2.1标识符的范围.