C 没有显式作用域解析,因此标识符(变量名称、typedef 名称、结构名称等)可以重用并被覆盖当打开一个新的范围时。当重复使用标识符时,该标识符所持有的先前上下文将不再存在visible.
在您的特定代码中,范围typedef
是全局的,因此 typedef 在编译包中的任何地方都可见。但是,您使用函数声明打开一个新作用域,并在该新作用域中定义一个变量,该变量使用与函数声明相同的标识符。typedef
。现在,该标识符指的是变量而不是类型;意思是,直到变量的范围结束(函数结束),typedef
是完全隐藏的。
回想一下,C 是线性编译的,因此您可以执行类似的操作来绕过所发生的屏蔽:
#include <stdio.h>
typedef int foo;
int main()
{
printf ("%zu\n", sizeof (foo)); /* #1 */
char foo;
printf ("%zu\n", sizeof foo); /* #2 */
return 0;
}
在第 1 点,请注意变量的范围char foo
由于编译器尚未达到其声明,因此尚未打开。 (编译器要做的就是在堆栈上为变量分配空间)。
所以使用foo
那时仍然参考全球定义的typedef
.
当您点击 #2 时,变量已被声明,并且变量的生命周期已正式开始,这意味着该标识符现在用于不同的实体。它屏蔽了当前块作用域(由函数声明开始)的全局定义foo
.
这是有据可查的行为;网上有 C 标准草案,但必须购买已发布的标准。草案第6.2.1条规定:
如果标识符指定同名的两个不同实体
空间,范围可能会重叠。如果是这样,一个实体的范围(
内部范围)将严格在另一个实体的范围之前结束
(外部范围)。在内部范围内,标识符指定
内部作用域中声明的实体;中声明的实体
外部作用域隐藏在内部作用域内(并且不可见)。source http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
请注意,这并不神奇或什么......这都是在编译时完成的。编译器有一个标识符表及其引用的内容,新的作用域会为这些创建新的表。碰巧的是,在上面代码中的第 1 点,编译器尚未用新的值填充表char foo
(这是由于线性编译造成的)。所以当它翻译第一个时printf
行,它会遍历所有活动范围以查找标识符foo
,并看到typedef
,并使用它。在第二次printf
,它会查看所有活动范围并找到该标识符的最近使用情况foo
并使用它。