标识符的命名空间
由同名标识符指定的不同实体要么具有不同的作用域,要么位于不同的命名空间(name spaces)。命名空间用于组织管理标识符的名称,避免标识符的名称发生冲突。同一命名空间内所有标识符名称都是唯一的。名称相同、命名空间不同的标识符是有效的;名称相同、命名空间相同的标识符可能会引发名称冲突。
对于不同类别的标识符,C语言提供了六个独立的命名空间,具体如下:
一、标签名(label names)
int labelOne; ... goto labelOne; ... labelOne: ...
goto语句中labelOne是标签名,与int labelOne;语句中的labelOne属于不同的命名空间,所以两个labelOne都是有效的。
二、结构、联合和枚举的标记(tag)
结构、联合、枚举的标记处于相同的命名空间,一个标识符可以用作结构、联合、枚举中任何一个的标记;但不能同时用作其中两个及两个以上的标记。
struct tagName{int money; int year; }; enum tagName{A, B, C, D}; //标记tagName发生冲突。
这里两个tagName标记属于同一命名空间,因此发生了名称冲突。
三、结构或者联合的成员
每个结构或者联合的成员都有单独的命名空间。
struct car{int year; int money; }; struct computer{int year; int money; };
上述成员名year,money不会发生冲突。
四、标准属性(standard attributes)和属性前缀(attribute prefixes)
ISO/IEC 9899:2024标准增加了属性相关的内容。属性指定了各种源构造(例如:类型、对象、标识符、块。)的附加信息。属性使用属性标记(attribute token)标识。属性标记包括标准属性和属性前缀标记(attribute prefixed token)。标准属性是ISO/IEC 9899:2024标准指定的属性;属性前缀标记是特定实现支持的属性。
ISO/IEC 9899:2024标准支持的标准属性如下表所示:
| 属性标记 | 属性 |
| deprecated | [[deprecated]] |
| fallthrough | [[fallthrough]] |
| maybe_unused | [[maybe_unused]] |
| nodiscard | [[nodiscard]] |
| noreturn | [[noreturn]] |
| _Noreturn | [[_Noreturn]] |
| reproducible | [[reproducible]] |
| unsequenced | [[unsequenced]] |
属性前缀标记语法格式如下所示:
attribute-prefix :: identifier
其中attribute-prefix是属性前缀,::是域解析符,identifier是尾随标识符,例如:gnu::const。
[[gnu::const]] [[nodiscard]] int func(int);
其中[[gnu::const]]是GCC编译器支持的属性;[[nodiscard]]是标准属性。
五、属性前缀标记中的尾随标识符(the trailing identifier in an attribute prefixed token)
每个属性前缀都有一个单独的命名空间,用于其引用的实现定义的属性,通过属性前缀和尾随标识符可以消除属性间的歧义,例如:[[deprecated]]和[[gnu::deprecated]],前者是标准属性,后者是实现定义的属性。
六、普通标识符(ordinary identifiers)
所有其它标识符称为普通标识符,包括在普通声明中声明的标识符以及声明为枚举成员的标识符。
int year; enum nationalDay {year, month, day}; //标识符year名称发生冲突。
这里两个标识符year命名空间相同,因此发生了名称冲突。
主要参考资料:
3、cppreference.com : Lookup and name spaces
4、learn.microsoft.com : Name Spaces
5、en.cppreference.com : Attribute specifier sequence
6、gcc.gnu.org : Attribute Syntax