【发布时间】:2011-12-09 15:45:02
【问题描述】:
如果我有这样的枚举:
enum EnumA
{
stuffA = 0
};
enum enumAA
{
stuffA = 1
};
当您引用stuffA 时会发生什么?我以为你会像在 Java 中那样将它们称为 EnumA.stuffA 和 EnumB.stuffA,但在 C 中似乎并非如此。
【问题讨论】:
如果我有这样的枚举:
enum EnumA
{
stuffA = 0
};
enum enumAA
{
stuffA = 1
};
当您引用stuffA 时会发生什么?我以为你会像在 Java 中那样将它们称为 EnumA.stuffA 和 EnumB.stuffA,但在 C 中似乎并非如此。
【问题讨论】:
enums 不引入新作用域。
在您的示例中,由于 stuffA 名称冲突,第二个 enum 无法编译。
为避免名称冲突,通常的做法是为enum 的元素提供一个公共前缀。不同的前缀将用于不同的枚举:
enum EnumA
{
EA_stuffA = 0
};
enum EnumAA
{
EAA_stuffA = 1
};
【讨论】:
enum enumA { enumA_stuffA = 0 }; enum enumAA { enumAA_stuffA = 1 };
枚举常量在全局命名空间中(更准确地说,普通标识符命名空间,与标签、标签和结构/联合成员命名空间形成对比),所以你会得到一个编译错误在第二个stuffA。
您不能在单个翻译单元中为同一个枚举名称使用两个不同的值(也不能两次指定相同的值)。
【讨论】:
正如其他人已经说过的,枚举常量在定义它们的实际范围内必须是唯一的。但是对于它们和其他标识符一样,允许在另一个范围内重新定义它们。例如。
enum EnumA
{
stuffA = 0
};
void func(void) {
enum enumAA
{
stuffA = 1
};
// do something
}
会好的。但是这种在不同范围内的重新定义往往是不受欢迎的,应该有据可查,否则你很快就会失去自己和他人。
【讨论】:
-Wshadow 的 gcc 提供了warning: declaration of 'stuffA' shadows a global declaration。
如前所述,这不会编译,因为 stuffA 被定义了两次。枚举值仅由枚举引用(即“stuffA”而不是 EnumA.stuffA)。您甚至可以在非枚举类型(例如整数)上使用它们。枚举有时会以这种方式与整数一起使用,类似于 #define 常量的方式。
【讨论】:
enum 常量是 int 根据标准的定义。
这个答案显示了 C 2018 的规则如何阻止相同的标识符被用作两个不同枚举的成员。这是一种语言律师的观点,旨在说明这一要求是如何从标准的语言中产生的。
6.2.3,“标识符的命名空间”告诉我们:
如果一个特定标识符的多个声明在翻译单元中的任何位置可见,则句法上下文消除了引用不同实体的用法的歧义。因此,不同类别的标识符有单独的名称空间,如下所示:
…
——所有其他标识符,称为普通标识符(在普通声明符中声明或作为枚举常量声明)。
因此,所有枚举常量和普通声明符都存在于一个名称空间中。 (上面省略的名称空间用于 labels [for goto statements];tags 结构、联合和枚举 [struct 之后的名称,如struct foo];以及结构或联合的成员 [每个都有自己的名称空间])。
6.7,“声明”,第 5 段告诉我们:
标识符的定义是该标识符的声明:
…
对于枚举常量,是标识符的(唯一)声明;
…
所以标准表明枚举常量只有一个定义。此外,6.2.1,“标识符的范围”,在第 1 段中告诉我们:
一个标识符可以表示一个对象;一个函数;结构、联合或枚举的标签或成员;类型定义名称;标签名称;宏名;或宏参数。同一个标识符可以在程序的不同点表示不同的实体。枚举的成员称为枚举常量。
请注意,这表明如果foo 标识了一个枚举常量,它就标识了一个枚举成员——它是一个特定枚举的特定成员。它不能同时识别enum A 的成员和enum B 的成员。因此,如果我们有代码:
enum A { foo = 1 };
enum B { foo = 1 };
在foo第二次出现时,它是enum A中foo的标识符,因此它不能是enum B的成员。
(关于标识符在不同点表示不同实体的句子是引入范围的概念。该子句中的其他段落解释了范围的概念和四种范围:函数、文件、块和函数原型。这些不影响上面的分析,因为上面的代码在一个范围内。)
【讨论】:
根据您声明这些枚举的位置,您还可以使用命名空间关键字声明新范围。
注意:我不建议这样做,我只是注意到这是可能的。 相反,最好使用其他示例中提到的前缀。
namespace EnumA
{
enum EnumA_e
{
stuffA = 0
};
};
namespace EnumAA
{
enum enumAA_e
{
stuffA = 1
};
};
【讨论】: