Можно ли использовать значение переменной в качестве метки для перехода goto?
Есть следующий код:
int main() {
goto s;
return 1;
s:
return 0;
}
Можно ли составить равносильный ему, но вместо фиксированного лейбла s использовать символ?
Псевдокод:
int main() {
char str = 's';
goto str;
return 1;
s:
return 0;
}
Нельзя использовать никакие условные операторы.
Ответы (3 шт):
Читаем стандарт:
8.7.5 The goto statement [stmt.goto]
The goto statement unconditionally transfers control to the statement labeled by the identifier. The identifier shall be a label (8.2) located in the current function.
Оператор goto безусловно передаёт управление инструкции, обозначенной идентификатором. Этот идентификатор должен быть меткой, расположенной в текущей функции.
Соответственно ни о каких переменных в goto речи быть не может. Более того, возможно одновременное существование меток и переменных с одинаковым названием:
#include <iostream>
int main() {
int p = 42;
goto p;
std::cout << "not printed\n";
p: std::cout << p << "\n";
}
Однако, как подсказали в комментарии, существует GNU расширение, позволяющее использовать метки как значения.
В компиляторе от GNU можно.
Только не напрямую, а используя символ в качестве индекса в массиве меток.
#include <stdio.h>
int
main (int ac, char *av[])
{
void *p[] = {&&L1, &&L2};
unsigned char c;
scanf("%c", &c);
goto *p[c & 1];
L1:
printf("'%c' is even code %d\n", c, c);
goto fin;
L2:
printf("'%c' is odd code %d\n", c, c);
fin:
return puts("End") == EOF;
}
Транслируем и запускаем
avp@avp-xubu2:~/hashcode$ g++ tf.c
avp@avp-xubu2:~/hashcode$ ./a.out
a
'a' is odd code 97
End
avp@avp-xubu2:~/hashcode$ ./a.out
b
'b' is even code 98
End
avp@avp-xubu2:~/hashcode$
P.S.
извините, но для всех символов мне было лень набивать...
Раз уж зашла речь об извращениях, вот решение для Visual C++ (только x86):
#include <iostream>
#include <map>
std::map<char, void*> labels;
#define GOTO(x) {void* tmp=labels[x];__asm {jmp tmp}}
#define SAVE_LABEL(x,name) {void* tmp;__asm {mov tmp, offset x};labels[name]=tmp;}
int main() {
SAVE_LABEL(s, 's');
SAVE_LABEL(p, 'p');
GOTO('p')
printf("Hello, world!\n");
s: printf("Hello from s!\n");
return 1;
p: printf("Hello from p!\n");
return 0;
}
Суть способа в том, что хоть goto и не может переходить к переменным, но вот инструкции безусловного перехода, которую он использует, абсолютно наплевать, к чему переходить.
Это приводит нас к такому ассемблерному коду для перехода
__asm {jmp x}
где x - произвольный адрес.
И к такому для получения адреса метки:
__asm {mov x, offset y}
где x - переменная, y - имя метки.
Но тут следует понимать, что ассемблерные вставки непереносимы и мешают компилятору оптимизировать код функции, в которой они присутствуют. Поэтому если вы хотели таким образом состряпать какое-то высокоэффективное решение с таблицами переходов, скорее всего, ничего не выйдет.