sentencias - comando goto cmd
¿Es posible almacenar la dirección de una etiqueta en una variable y usar goto para saltar a ella? (14)
Conozco la sensación, entonces todos dicen que no debería hacerse; solo tiene que hacerse. He aquí cómo hacerlo:
#define jumpto(a) asm("jmp *%0"::"r"(a):)
int main (void)
{
int i=1;
void* the_label_pointer;
the_label:
the_label_pointer = &&the_label;
if( i-- )
jumpto(the_label_pointer);
return 0;
}
El operador de desreferenciación de etiquetas && solo funcionará con gcc. Y, obviamente, la macro jumpto assembly debe implementarse específicamente para cada procesador (este funciona tanto con 32 como con 64 bit x86). También tenga en cuenta que no hay garantía de que el estado de la pila sea el mismo en dos puntos diferentes en la misma función. Y al menos con cierta optimización activada, es posible que el compilador asuma que algunos registros contienen algún valor en el punto posterior a la etiqueta. Este tipo de cosas pueden estropearse fácilmente y luego hacer una mierda loca que el compilador no espera. Asegúrese de revisar el código compilado.
Sé que todo el mundo odia a los gotos. En mi código, por razones que he considerado y con las que me siento cómodo, proporcionan una solución efectiva (es decir, no estoy buscando "no hacer eso" como respuesta, entiendo sus reservas y entiendo por qué las estoy usando). de todas formas).
Hasta ahora han sido fantásticos, pero quiero expandir la funcionalidad de tal manera que me obligue a poder almacenar punteros en las etiquetas, y luego ir a ellos más tarde.
Si este código funcionara, representaría el tipo de funcionalidad que necesito. Pero no funciona, y 30 minutos de Google no ha revelado nada. ¿Alguien tiene alguna idea?
int main (void)
{
int i=1;
void* the_label_pointer;
the_label:
the_label_pointer = &the_label;
if( i-- )
goto *the_label_pointer;
return 0;
}
De acuerdo con este hilo , los puntos de etiqueta no son un estándar, por lo tanto, si funcionan o no dependerán del compilador que está utilizando.
De acuerdo con el estándar C99, § 6.8.6, la sintaxis para un goto
es:
goto identifier ;
Entonces, incluso si pudiera tomar la dirección de una etiqueta, no podría usarla con goto.
Podría combinar un goto
con un switch
, que es como un goto
calculado, para un efecto similar:
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;
}
Si usas esto para algo que no sea un concurso de C ofuscado, te perseguiré y haré daño.
El switch ... case
declaración del switch ... case
es esencialmente un goto
calculado . Un buen ejemplo de cómo funciona es el truco extraño conocido como Dispositivo de Duff :
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);
}
}
No puede hacer un goto
desde una ubicación arbitraria utilizando esta técnica, pero puede ajustar toda su función en una instrucción switch
basada en una variable, luego establecer esa variable indicando a dónde quiere ir, y pasar a esa instrucción switch.
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;
}
}
Por supuesto, si haces esto mucho, querrás escribir algunas macros para envolverlo.
Esta técnica, junto con algunas macros de conveniencia, incluso se puede usar para implementar corutinas en C.
En la versión muy antigua del lenguaje C (piense en el tiempo que los dinosaurios vagabundeaban por la Tierra), conocida como versión "C Reference Manual" (que hace referencia a un document escrito por Dennis Ritchie), las etiquetas tenían formalmente "array of int" (extraño, pero cierto), lo que significa que puede declarar una variable int *
int *target;
y asignar la dirección de la etiqueta a esa variable
target = label; /* where `label` is some label */
Más tarde podría usar esa variable como el operando de la declaración goto
goto target; /* jumps to label `label` */
Sin embargo, en ANSI C esta característica fue descartada. En la C estándar moderna no se puede tomar la dirección de una etiqueta y no se puede hacer goto
"parametrizado". Se supone que este comportamiento se simula con instrucciones de switch
, punteros a funciones y otros métodos, etc. En realidad, incluso el "Manual de referencia de C" decía que "las variables de etiqueta son una mala idea en general; la declaración de cambio las hace casi siempre innecesarias". "(ver document ).
La única cosa oficialmente admitida que puedes hacer con una etiqueta en C es goto
ella. Como habrás notado, no puedes tomar la dirección ni almacenarla en una variable ni en ninguna otra cosa. Entonces, en lugar de decir "no hagas eso", voy a decir "no puedes hacer eso".
Parece que tendrá que encontrar una solución diferente. ¿Tal vez el lenguaje ensamblador, si esto es crítico para el rendimiento?
Lea esto: setjmp.h - Wikipedia Como se dijo anteriormente, es posible con setjmp / longjmp con el que puede almacenar un punto de salto en una variable y retroceder más adelante.
Los estándares C y C ++ no son compatibles con esta característica. Sin embargo, la Colección de compiladores de GNU (GCC) incluye una extensión no estándar para hacer esto como se describe en este artículo . Esencialmente, han agregado un operador especial "&&" que informa la dirección de la etiqueta como tipo "void *". Ver el artículo para más detalles.
PD En otras palabras, simplemente use "&&" en lugar de "&" en su ejemplo, y funcionará en GCC.
PPS. Sé que no quieres que lo diga, pero lo diré de todos modos ... ¡NO HAGAS ESO!
Notaré que el funcionalmente descrito aquí (incluyendo && en gcc) es IDEAL para implementar un intérprete de lenguaje Forth en C. Eso elimina todos los argumentos de "do not do that" del agua - el ajuste entre esa funcionalidad y la forma El intérprete interno de Forth funciona demasiado bien como para ignorarlo.
Puede asignar etiquetas a variable usando &&. Aquí está su código modificado.
int main (void)
{
int i=1;
void* the_label_pointer = &&the_label;
the_label:
if( i-- )
goto *the_label_pointer;
return 0;
}
Puedes hacer algo como la computadora de Fortran con punteros a funciones.
// variables globales aquí
void c1 () {// fragmento de código
}
void c2 () {// fragmento de código
}
void c3 () {// fragmento de código
}
void (* goTo [3]) (void) = {c1, c2, c3};
// entonces
int x = 0;goTo [x ++] ();
goTo [x ++] ();
goTo [x ++] ();
Puedes hacer algo similar con 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;
}
Use punteros de funciones y un ciclo while. No hagas una pieza de código que alguien más tendrá que arrepentirse de arreglar para ti.
Supongo que está intentando cambiar la dirección de la etiqueta de alguna manera externa. Los punteros de función funcionarán.
#include <stdio.h>
int main(void) {
void *fns[3] = {&&one, &&two, &&three};
char p;
p = -1;
goto start; end: return 0;
start: p++;
goto *fns[p];
one: printf("hello ");
goto start;
two: printf("World. /n");
goto start;
three: goto end;
}