Почему GCC определяет унарный оператор '&&' вместо использования '&'?
Как обсуждалось в этом вопросе, GCC определяет нестандартный унарный оператор &&
, чтобы взять адрес метки.
Почему он определяет новый оператор вместо использования существующей семантики оператора &
и/или семантики функций (где foo
и &foo
оба дают адрес функции foo()
)?
Ответы
Ответ 1
Имена ярлыков не мешают другим идентификаторам, потому что они используются только в gotos. Переменная и метка могут иметь одно и то же имя, а в стандартных 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
Ответ 2
GCC добавил это расширение , которое будет использоваться при инициализации статического массива, который будет служить таблицей переходов:
static void *array[] = { &&foo, &&bar, &&hack };
Где foo
, bar
и hack
- метки. Затем метку можно выбрать с индексированием, например:
goto *array[i];
Стандарт говорит, что
C11: 6.2.1 Области идентификаторов (p1):
Идентификатор может обозначать объект; функция; тег или член структуры, объединения или перечисления; имя typedef; a имя метки; имя макроса; или макропараметр.
Далее говорится в разделе 6.2.3:
Если более чем одно объявление определенного идентификатора видимо в любой точке единицы перевода, синтаксический контекст неоднозначно использует использование, относящееся к различным объектам. Таким образом, существуют отдельные пространства имен для различных категорий идентификаторов, а именно:
- имена ярлыков (неясно, с синтаксисом объявления и использования метки);
- теги структур, объединений и перечислений (неоднозначно следуя любым 32) ключевых слов struct
, union
или enum
);
- члены структур или союзов; каждая структура или объединение имеет отдельное пространство имен для своих членов (неоднозначно по типу выражения, используемого для доступа к элементу через оператор .
или ->
);
- все остальные идентификаторы, называемые обычными идентификаторами (объявляются в обычных деклараторах или в качестве констант перечисления).
Это означает, что объект и метка могут быть обозначены одним и тем же идентификатором. На этом этапе, чтобы компилятор знал, что адрес foo
является адресом метки, а не адресом объекта foo
(если существует), GCC определил оператор &&
для адреса метки.