正如在这个问题中所讨论的,GCC 定义了非标准的一元运算符&&
来获取标签的地址。
为什么它定义一个新的运算符,而不是使用运算符的现有语义&
和/或函数的语义(其中foo
和&foo
两者都产生函数的地址foo()
)?
正如在这个问题中所讨论的,GCC 定义了非标准的一元运算符&&
来获取标签的地址。
为什么它定义一个新的运算符,而不是使用运算符的现有语义&
和/或函数的语义(其中foo
和&foo
两者都产生函数的地址foo()
)?
标签名称不会干扰其他标识符,因为它们仅在 goto 中使用。变量和标签可以具有相同的名称,并且在标准 C 和 C++ 中,从上下文中总是可以清楚地了解其含义。所以这是完全有效的:
name:
int name;
name = 4; // refers to the variable
goto name; // refers to the label
因此需要区分 & 和 && 以便编译器知道期望什么样的名称:
&name; // refers to the variable
&&name; // refers to the label
GCC 添加了这个扩展来初始化一个用作跳转表的静态数组:
static void *array[] = { &&foo, &&bar, &&hack };
其中foo
和是bar
标签hack
。然后可以使用索引选择标签,如下所示:
goto *array[i];
标准说
一个标识符可以表示一个对象;一个函数;结构、联合或枚举的标签或成员;类型定义名称;标签名称;宏名;或宏参数。
此外,它在第 6.2.3 节中说:
如果一个特定标识符的多个声明在翻译单元中的任何位置可见,则句法上下文消除了引用不同实体的用法的歧义。因此,各种类别的标识符都有单独的名称空间,如下所示:
—标签名称(通过标签声明和使用的语法消除歧义);
—关键字, , 或的结构、联合和枚举(通过以下任意32 个消除歧义)的标签;
struct
union
enum
——结构或工会的成员;
.
每个结构或联合对其成员都有一个单独的名称空间(通过用于通过or运算符访问成员的表达式的类型来消除歧义->
);— 所有其他标识符,称为普通标识符(在普通声明符中声明或作为枚举常量声明)。
这意味着一个对象和一个标签可以用相同的标识符来表示。此时,为了让编译器知道的地址foo
是标签的地址,而不是对象的地址foo
(如果存在),GCC&&
为标签的地址定义了操作符。