Действительно ли возможно сохранить адрес маркировки в переменной и использовать goto для перехода к нему?

Поскольку проверка переполнение занимает время. Каждая примитивная математическая операция, которая обычно переводит в единственную инструкцию по сборке, должна была бы включать проверку на переполнение, приводящее к нескольким инструкциям по сборке, потенциально приводящим к программе, которая несколько раз медленнее.

53
задан Ciro Santilli 新疆改造中心法轮功六四事件 24 June 2015 в 00:20
поделиться

9 ответов

Стандарты C и C ++ не поддерживают эту функцию. Однако коллекция компиляторов GNU (GCC) включает нестандартное расширение для этого, как описано в этой статье . По сути, они добавили специальный оператор «&&», который сообщает адрес метки как тип «void *». Подробнее см. В статье.

PS Другими словами, просто используйте «&&» вместо «&» в вашем примере, и это будет работать в GCC.
PPS Я знаю, что вы не хотите, чтобы я это говорил, но я все равно скажу, ... НЕ ДЕЛАЙТЕ ЭТО !!!

60
ответ дан 7 November 2019 в 08:26
поделиться

Согласно этого потока , точки меток не являются стандартом, поэтому работают они или нет, будет зависеть от используемого вами компилятора.

0
ответ дан 7 November 2019 в 08:26
поделиться

Прочтите это: setjmp.h - Википедия Как было сказано ранее, это возможно с setjmp / longjmp, с помощью которого вы можете сохранить точку перехода в переменной и вернуться к ней позже.

1
ответ дан 7 November 2019 в 08:26
поделиться

Используйте указатели на функции и цикл while. Не создавайте фрагмент кода, кому-то другому придется пожалеть об исправлении за вас.

Я предполагаю, что вы пытаетесь каким-то образом изменить адрес метки извне. Указатели функций будут работать.

4
ответ дан 7 November 2019 в 08:26
поделиться

В очень-очень старой версии языка C (вспомните время, когда динозавры бродили по Земле), известной как версия «Справочного руководства C» (которая относится к документу ] написано Деннисом Ричи), метки формально имели тип «массив int» (странно, но верно), что означало, что вы могли объявить переменную int *

int *target;

и присвоить этой переменной адрес метки.

target = label; /* where `label` is some label */

Позже вы могли использовать эту переменную в качестве операнда оператора goto

goto target; /* jumps to label `label` */

Однако в ANSI C эта функция была исключена. В стандартном современном C вы не можете брать адрес метки и не можете делать "параметризованный" goto . Предполагается, что такое поведение моделируется с помощью операторов switch , указателей на функции и других методов и т. Д. На самом деле, даже " В самом справочном руководстве C говорилось, что «метки переменных - плохая идея; оператор switch делает их почти всегда ненужными »(см. « 14.4 Метки »).

10
ответ дан 7 November 2019 в 08:26
поделиться

Согласно стандарту C99, § 6.8.6, синтаксис для goto таков:

    goto identifier ;

Итак, даже если вы могли бы взять адрес метки, вы не могли использовать его с goto.

Вы можете объединить goto с переключателем , который похож на вычисляемый goto , для аналогичного эффекта :

int foo() {
    static int i=0;
    return i++;
}

int main(void) {
    enum {
        skip=-1,
        run,
        jump,
        scamper
    } label = skip; 

#define STATE(lbl) case lbl: puts(#lbl); break
    computeGoto:
    switch (label) {
    case skip: break;
        STATE(run);
        STATE(jump);
        STATE(scamper);
    default:
        printf("Unknown state: %d\n", label);
        exit(0);
    }
#undef STATE
    label = foo();
    goto computeGoto;
}

Если вы будете использовать это для чего-нибудь, кроме замаскированного конкурса C, я выслежу вас и причиню вам боль.

12
ответ дан 7 November 2019 в 08:26
поделиться

Оператор switch ... case по сути является вычисленным goto . Хорошим примером того, как это работает, является странный хак, известный как Устройство Даффа :

send(to, from, count)
register short *to, *from;
register count;
{
    register n=(count+7)/8;
    switch(count%8){
    case 0: do{ *to = *from++;
    case 7:     *to = *from++;
    case 6:     *to = *from++;
    case 5:     *to = *from++;
    case 4:     *to = *from++;
    case 3:     *to = *from++;
    case 2:     *to = *from++;
    case 1:     *to = *from++;
        }while(--n>0);
    }
}

Вы не можете выполнить goto из произвольного места, используя эту технику, но вы можете обернуть всю вашу функцию в операторе switch на основе переменной, затем установите эту переменную, указывающую, куда вы хотите перейти, и goto этот оператор переключения.

int main () {
  int label = 0;
  dispatch: switch (label) {
  case 0:
    label = some_computation();
    goto dispatch;
  case 1:
    label = another_computation();
    goto dispatch;
  case 2:
    return 0;
  }
}

Конечно, если вы это сделаете это много, вам нужно написать несколько макросов, чтобы обернуть это.

Этот метод, вместе с некоторыми вспомогательными макросами, можно даже использовать для реализации сопрограмм в C .

10
ответ дан 7 November 2019 в 08:26
поделиться

Вы можете сделать нечто подобное с помощью setjmp / longjmp.

int main (void)
{
    jmp_buf buf;
    int i=1;

    // this acts sort of like a dynamic label
    setjmp(buf);

    if( i-- )
        // and this effectively does a goto to the dynamic label
        longjmp(buf, 1);

    return 0;
}
15
ответ дан 7 November 2019 в 08:26
поделиться

Единственная официально поддерживаемая вещь, которую вы можете делать с меткой в ​​C, - это goto it. Как вы заметили, вы не можете взять его адрес, сохранить его в переменной или что-то еще. Поэтому вместо того, чтобы сказать «не делайте этого», я скажу «вы не можете этого сделать».

Похоже, вам придется найти другое решение. Может быть, язык ассемблера, если это критично для производительности?

3
ответ дан 7 November 2019 в 08:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: