Lookup and name spaces
当在C程序中遇到 标识符 时,会执行查找操作以定位引入该标识符且当前 在作用域内 的 声明 。C语言允许多个相同标识符的声明同时存在于作用域中,只要这些标识符属于不同的类别,即称为 命名空间 :
|
4)
全局属性命名空间:由标准定义的
属性标记
或实现定义的属性前缀。
5)
非标准属性名称:跟随属性前缀的属性名称。每个属性前缀为其引入的实现定义属性拥有独立的命名空间。
|
(C23起) |
在查找时,标识符的命名空间由其使用方式决定:
|
4)
直接在属性说明符(
[
[
...
]
]
)中出现的标识符会在全局属性命名空间中查找。
5)
在属性前缀后跟随
::
标记后的标识符会在该属性前缀引入的命名空间中查找。
|
(since C23) |
目录 |
注释
宏的名称不属于任何命名空间,因为它们会在语义分析之前由预处理器进行替换。
通常的做法是使用 typedef 声明将结构体/联合体/枚举名称注入到普通标识符的命名空间中:
struct A { }; // 在标签命名空间引入名称 A typedef struct A A; // 首先,在"struct"后的 A 查找会在标签命名空间找到一个 // 然后在普通命名空间引入名称 A struct A* p; // 正确,此 A 在标签命名空间中被查找 A* q; // 正确,此 A 在普通命名空间中被查找
一个著名的在同一标识符在两个命名空间中被使用的例子是来自POSIX头文件
sys/stat.h
的标识符
stat
。当作为普通标识符使用时,它
命名一个函数
;而当作为标签使用时,它
表示一个结构体
。
与 C++ 不同,枚举常量不是结构体成员,其命名空间属于普通标识符的命名空间,并且由于 C 语言中没有结构体作用域,它们的作用域就是结构体声明出现的作用域:
struct tagged_union { enum {INT, FLOAT, STRING} type; union { int integer; float floating_point; char *string; }; } tu; tu.type = INT; // 在C语言中有效,在C++中错误
|
如果标准属性、属性前缀或非标准属性名称不受支持,该无效属性本身将被忽略而不会引发错误。 |
(since C23) |
示例
void foo (void) { return; } // 普通命名空间,文件作用域 struct foo { // 标签命名空间,文件作用域 int foo; // 此结构体foo的成员命名空间,文件作用域 enum bar { // 标签命名空间,文件作用域 RED // 普通命名空间,文件作用域 } bar; // 此结构体foo的成员命名空间,文件作用域 struct foo* p; // 正确:使用标签/文件作用域名称"foo" }; enum bar x; // 正确:使用标签/文件作用域bar // int foo; // 错误:普通命名空间foo已在作用域中 //union foo { int a, b; }; // 错误:标签命名空间foo已在作用域中 int main(void) { goto foo; // 正确:使用标签命名空间/函数作用域中的"foo" struct foo { // 标签命名空间,块作用域(隐藏文件作用域) enum bar x; // 正确:使用标签命名空间/文件作用域中的"bar" }; typedef struct foo foo; // 正确:使用标签命名空间/块作用域中的foo // 定义块作用域普通foo(隐藏文件作用域) (foo){.x=RED}; // 使用普通/块作用域foo和普通/文件作用域RED foo:; // 标签命名空间,函数作用域 }
参考文献
- C17 标准 (ISO/IEC 9899:2018):
-
- 6.2.3 标识符的名称空间 (p: 29-30)
- C11标准(ISO/IEC 9899:2011):
-
- 6.2.3 标识符的名称空间(页码:37)
- C99标准(ISO/IEC 9899:1999):
-
- 6.2.3 标识符的名称空间(页码:31)
- C89/C90 标准 (ISO/IEC 9899:1990):
-
- 3.1.2.3 标识符的命名空间
参见
|
C++ 文档
关于
Name lookup
|