6.2.1 标识符、类型名和复合字面量的作用域
IMPORTANT
这一节是后面理解声明、隐藏、同名、标签、typedef、复合字面量的基础。很多“为什么这里这个名字指的不是那个东西”的问题,都要回到作用域来解释。
一个标识符可以表示:
- 标准属性、属性前缀或属性名;
- 对象;
- 函数;
- 结构体、联合体或枚举的标记,或者它们的成员;
typedef名;- 标号名;
- 宏名;
- 或宏形参。
同一个标识符,在程序的不同位置可以表示不同实体。枚举中的成员称为枚举常量。这里不再进一步讨论宏名和宏形参,因为在程序翻译进入语义阶段之前,源文件中出现的宏名都已经被替换为构成其宏定义的预处理记号序列。
对于某个标识符所表示的每一个不同实体,该标识符都只会在程序文本中的某个区域内可见(即可以使用);这个区域称为它的作用域。由同一标识符表示的不同实体,要么拥有不同作用域,要么位于不同命名空间中。作用域共有四种:
- 函数作用域;
- 文件作用域;
- 块作用域;
- 函数原型作用域。
这里的函数原型,是指对函数作出的声明。
标号名是唯一具有函数作用域的标识符种类。它可以在其所在函数中的任意位置(通过
goto语句)使用,并且通过它在语法上的出现形式被隐式声明,即:后面跟着一个冒号:和一条语句。其他所有标识符的作用域,都由其声明出现的位置决定(出现在声明符或类型说明符中)。
如果声明该标识符的声明符或类型说明符,出现在任何块和任何形参列表之外,则该标识符具有文件作用域,其作用域在翻译单元结束处终止。
如果声明该标识符的声明符或类型说明符,出现在某个块内部,或者出现在函数定义中的形参声明列表中,则该标识符具有块作用域,其作用域在与之关联的块结束处终止。
如果声明该标识符的声明符或类型说明符,出现在函数原型中的形参声明列表内(而不是函数定义的一部分),则该标识符具有函数原型作用域,其作用域在函数声明符结束处终止。
如果一个标识符在同一命名空间内表示两个不同实体,那么它们的作用域可以重叠。如果发生重叠,其中一个实体的作用域(内层作用域)一定会严格早于另一个实体的作用域(外层作用域)结束。在内层作用域中,该标识符表示的是内层作用域里声明的实体;外层作用域中的那个实体在内层作用域内会被隐藏,因此不可见。
除非明确另有说明,当本文档用“标识符”一词来指代某个实体时(而不是指语法构造本身),它指的是在该标识符出现位置上、相关命名空间中其声明可见的那个实体。
当且仅当两个标识符的作用域终止于同一点时,它们才具有相同作用域。
结构体、联合体和枚举的标记,其作用域从声明该标记的类型说明符中该标记出现之后立刻开始。每个枚举常量的作用域,从定义该枚举量的枚举项在枚举项列表中出现之后立刻开始。
具有欠说明定义(underspecified definition)的普通标识符,其作用域从该定义完成时开始;如果同一个普通标识符还声明了另一个实体,且后者的作用域包围当前块,那么一旦内层声明符完成,那个外层声明就会被隐藏。19)其他任何标识符的作用域,都从其声明符完成之后立刻开始。
作为特例,类型名(它并不是某个标识符的声明)被视为拥有一种作用域:这个作用域从类型名中本来应出现、但被省略的那个标识符位置之后立刻开始。复合字面量(它是一种用于访问匿名对象的表达式)与其定义中使用的类型名的作用域相关联;该作用域可能是文件作用域、函数原型作用域或块作用域。
脚注说明
19)这意味着,外层声明对初始化器来说是不可见的。
前向引用:声明(6.7)、函数调用(6.5.3.3)、复合字面量(6.5.3.6)、函数定义(6.9.2)、标识符(6.4.2)、宏替换(6.10.5)、标识符的命名空间(6.2.3)、源文件包含(6.10.3)、语句和块(6.8)。