Я видел программу на C с таким кодом:

static void *arr[1]  = {&& varOne,&& varTwo,&& varThree};

varOne: printf("One") ;
varTwo: printf("Two") ;
varThree: printf("Three") ;

Я не понимаю, что делает &&, потому что слева от него ничего нет. По умолчанию он оценивается как null? Или это частный случай?

Изменить: добавлена ​​дополнительная информация, чтобы сделать вопрос / код более понятным для моего вопроса. Спасибо всем за помощь. Это был случай расширения gcc.

50
Joshua 7 Сен 2016 в 00:09

3 ответа

Лучший ответ

Это специфичное для gcc расширение, унарный оператор &&, который можно применить к имени метки, возвращая его адрес в виде значения void*.

Как часть расширения разрешено goto *ptr;, где ptr - выражение типа void*.

Это задокументировано здесь в руководстве по gcc.

Вы можете получить адрес метки, определенной в текущей функции (или содержащая функция) с унарным оператором &&. Значение имеет введите void *. Это значение является постоянным и может использоваться везде, где константа этого типа действительна. Например:

void *ptr;
/* ... */
ptr = &&foo;

Чтобы использовать эти значения, вы должны иметь возможность перейти к одному из них. Готово с вычисленным оператором goto, goto *exp;. Например,

goto *ptr;

Допускается любое выражение типа void *.

Как указывает zwol в комментарии, gcc использует && вместо более очевидного &, потому что метка и объект с тем же именем могут быть видны одновременно, что делает &foo потенциально неоднозначным, если { {X3}} означает «адрес метки». Имена меток занимают собственное пространство имен (не в смысле C ++) и могут появляться только в определенных контекстах: определяемых label-statement , как цель оператора goto, или, для gcc как операнд унарного &&.

61
Keith Thompson 7 Сен 2016 в 18:19

Это расширение 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
16
M.M 7 Сен 2016 в 20:30

Я не знаю ни одного оператора, который работал бы таким образом в C. В зависимости от контекста, амперсанд в C может означать много разных вещей.

Address-Of оператора

Прямо перед lvalue, например

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 } };

Логическое И

Логический оператор И более простой, в отличие от оператора выше, это бинарный оператор, то есть для него требуются левый и правый операнды. Он работает следующим образом: вычисляет левый и правый операнды и возвращает истину, если оба значения истинны, или больше 0, если они не являются логическими.

bool flag = true;
bool flag2 = false;
if (flag && flag2) {
    // Not evaluated
}
flag2 = true;
if (flag && flag2) {
   // Evaluated
}

Побитовое И…

Еще одно использование амперсанда в C - побитовое И. Он похож на логический оператор AND, за исключением того, что он использует только один амперсанд и выполняет операцию AND на битовом уровне.

Предположим, у нас есть число и оно отображается в двоичном представлении, показанном ниже, операция 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

В мире C ++ все становится сложнее. Амперсанд может быть помещен после типа для обозначения ссылочного типа (вы можете рассматривать его как менее мощный, но безопасный вид указателя), тогда все становится еще сложнее с 1) ссылкой на r-значение, когда два амперсанда помещаются после тип. 2) Универсальные ссылки, когда два амперсанда помещаются после типа шаблона или автоматически вычитаемого типа.

Я думаю, ваш код, вероятно, компилируется только в вашем компиляторе из-за каких-то расширений. Я думал об этом https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C но я сомневаюсь, что это так.

-6
Fred Garnier 6 Сен 2016 в 21:50