Что делает оператор &&, когда в C нет левой стороны?
Я видел программу на C, у которой был код вроде следующего:
static void *arr[1] = {&& varOne,&& varTwo,&& varThree};
varOne: printf("One") ;
varTwo: printf("Two") ;
varThree: printf("Three") ;
Я смущен тем, что делает &&
, потому что ничего не осталось от него. Означает ли он по умолчанию значение null? Или это особый случай?
Изменить:
Добавлена дополнительная информация, чтобы сделать вопрос/код более понятным для моего вопроса.
Спасибо всем за помощь. Это был случай расширения gcc.
Ответы
Ответ 1
Это gcc-специфическое расширение, унарный оператор &&
, который может быть применен к имени метки, присваивая свой адрес значению void*
.
В качестве части расширения допускается goto *ptr;
, где ptr
- выражение типа void*
.
В документе gcc описано здесь.
Вы можете получить адрес метки, определенной в текущей функции (или содержащая функция) с унарным оператором &&
. Значение имеет тип void *
. Это значение является константой и может использоваться везде, где константа этого типа действительна. Например:
void *ptr;
/* ... */
ptr = &&foo;
Чтобы использовать эти значения, вы должны иметь возможность переходить на один. Готово с вычисленным оператором goto, goto *exp;
. Например,
goto *ptr;
Разрешено любое выражение типа void *
.
Как отмечает zwol в комментарии, gcc использует &&
, а не более очевидный &
, потому что метка и объект с тем же именем могут быть видны одновременно, делая &foo
потенциально неоднозначным, если &
означает "адрес метки". Названия ярлыков занимают собственное пространство имен (не в смысле С++) и могут отображаться только в определенных контекстах: определены с помощью labeled-statement, в качестве цели оператора goto
или для gcc, в качестве операнда унарного &&
.
Ответ 2
Это расширение gcc, известное как "Ярлыки как значения". Ссылка на документацию gcc.
В этом расширении &&
является унарным оператором, который может быть применен к метке. Результатом является значение типа void *
. Это значение позже может быть разыменовано в операторе goto
, чтобы заставить выполнение перейти к этой метке. Кроме того, для этого значения допускается арифметика указателя.
Метка должна быть в той же функции; или в закрывающей функции, если код также использует расширение gcc "вложенных функций".
Вот пример программы, в которой эта функция используется для реализации конечного автомата:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
void *tab[] = { &&foo, &&bar, &&qux };
// Alternative method
//ptrdiff_t otab[] = { &&foo - &&foo, &&bar - &&foo, &&qux - &&foo };
int i, state = 0;
srand(time(NULL));
for (i = 0; i < 10; ++i)
{
goto *tab[state];
//goto *(&&foo + otab[state]);
foo:
printf("Foo\n");
state = 2;
continue;
bar:
printf("Bar\n");
state = 0;
continue;
qux:
printf("Qux\n");
state = rand() % 3;
continue;
}
}
Компиляция и выполнение:
$ gcc -o x x.c && ./x
Foo
Qux
Foo
Qux
Bar
Foo
Qux
Qux
Bar
Foo
Ответ 3
Я не знаю ни одного оператора, который работает таким образом в C.
В зависимости от контекста амперсанд в C может означать много разных вещей.
Оператор Address-Of
Прямо перед значением l, например
int j;
int* ptr = &j;
В приведенном выше коде ptr хранится адрес j, и в этом контексте берется адрес любого lvalue. Приведенный ниже код имел бы смысл для меня, если бы он был написан таким образом.
static int varOne;
static int varTwo;
static int varThree;
static void *arr[1][8432] = { { &varOne,&varTwo, &varThree } };
Логическое И
Логический оператор AND более прост, в отличие от оператора выше, это двоичный оператор, то есть он требует левого и правого операнда. То, как это работает, - это оценить левый и правый операнды и вернуть true, если оба они истинны или больше 0, если они не являются bool.
bool flag = true;
bool flag2 = false;
if (flag && flag2) {
// Not evaluated
}
flag2 = true;
if (flag && flag2) {
// Evaluated
}
Побитовое И
Другое использование амперсанда в C выполняет побитовое И. Он аналогичен логическому оператору AND, за исключением того, что он использует только один амперсанд и выполняет операцию И на уровне бит.
Предположим, что у нас есть число, и что он отображает двоичное представление, показанное ниже, операция И работает так:
0 0 0 0 0 0 1 0
1 0 0 1 0 1 1 0
---------------
0 0 0 0 0 0 1 0
На земле С++ все усложняется. Амперсанд может быть помещен после типа, чтобы обозначить ссылочный тип (вы можете думать о нем как о менее мощном, но безопасном виде указателя), тогда все становится еще сложнее с 1) ссылкой на значение r, когда два амперсанда помещаются после тип. 2) Универсальные ссылки, когда два амперсанда размещаются после типа шаблона или автоматически вычитаемого типа.
Я думаю, что ваш код, вероятно, компилируется только в вашем компиляторе из-за расширения каких-то ролей. Я думал об этом https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C, но я сомневаюсь в этом.