2009-06-02 10 views
17

Quiero declarar una matriz de "etiquetas de salto".Cómo almacenar las etiquetas de goto en una matriz y luego saltar a ellas?

Luego quiero saltar a una "etiqueta de salto" en esta matriz.

Pero no tengo ni idea de cómo hacerlo.

Debe tener un aspecto como el siguiente código:

function() 
{ 
    "gotolabel" s[3]; 
    s[0] = s0; 
    s[1] = s1; 
    s[2] = s2; 

    s0: 
    .... 
    goto s[v]; 

    s1: 
    .... 
    goto s[v]; 

    s2: 
    .... 
    goto s[v]; 
} 

¿Alguien tiene una idea de cómo llevar a cabo esto?

+3

Si es una tarea no te recomiendo que vayas de esta manera; a tu profesor no le gustaría. – qrdl

+2

no, no, tengo diecisiete años e intento escribir un tokenizador simple y rápido. Pensé en acelerarlo de esta manera. –

+2

Menciona en los comentarios a continuación que puede haber 2 ** 16 casos. ¿Será esta la norma? Si es así, cambia el problema dramáticamente. –

Respuesta

38

Es posible con la función GCC conocida como "labels as values".

void *s[3] = {&&s0, &&s1, &&s2}; 

if (n >= 0 && n <=2) 
    goto *s[n]; 

s0: 
... 
s1: 
... 
s2: 
... 

Sólo funciona con GCC!

+0

gracias! esto es exactamente lo que quería saber! –

+0

si esta es la respuesta correcta, ¡márcalo como una respuesta! –

+3

+1 por exponer rarezas (blech, gack, yuck!) –

11

No hay forma directa de almacenar las direcciones de código para saltar a C. ¿Qué le parece usar el conmutador?

#define jump(x) do{ label=x; goto jump_target; }while(0) 
int label=START; 
jump_target: 
switch(label) 
{ 
    case START: 
     /* ... */ 
    case LABEL_A: 
     /* ... */ 
} 

Puede encontrar un código similar producido por cada generador de analizador/máquina de estados sin pila. Tal código no es fácil de seguir, así que a menos que se genere código o su problema sea más fácilmente descrito por la máquina de estado, recomendaría que no lo haga.

+1

Creo que quiere soltar el 'goto' delante de la etiqueta' jump_target' – Christoph

+0

derecha, corregido ahora – lispmachine

8

¿podría usar los punteros a las funciones en lugar de ir a?

De esta manera puede crear una serie de funciones para llamar y llamar al apropiado.

+0

Sé que puedo hacerlo con punteros a función. Pero esto sería lento, porque tendría que llamar a una función a menudo. ¡Creo que el costo de la llamada sería grande! –

+7

@youllknow: Las palabras "Creo" en el comentario anterior me dicen que estás en peligro real de caer en la "optimización prematura". El primer objetivo debe ser comenzar con una solución clara de "trabajo" y luego optimizarla según sea necesario. Considere esto: solo 1 compilador tiene esta característica como una extensión, sin embargo, cada compilador C/C++ usa máquinas de estado. Si esta es la mejor manera de resolver este problema, ¿por qué no todos los compiladores tienen esta característica? –

+0

@Richard Corden: ¿Entonces piensas que la mejora de velocidad es muy baja? Pensé en la matriz de punteros de función también. El problema es que las funciones se llamarían muy a menudo, pero solo hacen pequeñas cosas. Así que pensé que llamar a la función podría ser más costoso que lo que hace la función. Puedo implementar mi problema con los punteros de función, pero pensé que podía acelerarlo con el "método goto". ¿Cuál es tu opinión? –

6

En la norma estándar C, esto no es posible hasta donde yo sé. Sin embargo, hay una extensión en el compilador de GCC, documented here, que hace esto posible.

La extensión presenta el nuevo operador &&, para tomar la dirección de una etiqueta, que luego se puede utilizar con la instrucción goto.

+0

Interesante, no lo sabía. Gracias. –

+0

sí, muy bien !, gracias! –

16

goto necesita una etiqueta en tiempo de compilación.

En este ejemplo, parece que está implementando algún tipo de máquina de estado. Lo más común es que se implementan como un switch de los casos constructo:

while (!finished) switch (state) { 
    case s0: 
    /* ... */ 
    state = newstate; 
    break; 

    /* ... */ 
} 

Si necesita que sea más dinámico, utilice una matriz de punteros de función.

+0

Merece la pena señalar que 'switch' es para expresiones de casos compactos que se implementa con una tabla de salto que es básicamente lo mismo que una matriz o etiquetas: http://stackoverflow.com/questions/14067547/how-switch-case-statement-implemented -o-funciona-internamente –

+0

+1 para "utilizar una matriz de indicadores de función". o hoy en día, una matriz de 'std :: function' almacenando lambdas podría proporcionar una sintaxis mucho más compacta. –

5

Para eso están las declaraciones switch.

switch (var) 
{ 
case 0: 
    /* ... */ 
    break; 
case 1: 
    /* ... */ 
    break; 
default: 
    /* ... */ 
    break; /* not necessary here */ 
} 

Tenga en cuenta que el compilador no lo traduce necesariamente en una tabla de salto.

Si realmente desea construir la tabla de salto usted mismo, puede utilizar una matriz de punteros a función.

+0

Fue solo un ejemplo simple ... En mi opción, el interruptor es lento si tengo 2^16 casos? ¿No es así? –

+3

@youllknow: a menudo los buenos compiladores optimizarán un interruptor denso en una tabla de salto para ti. Entonces, no, los interruptores no son necesariamente lentos. – user83255

2

No se puede hacer con un goto: las etiquetas tienen que ser identificadores, no variables o constantes. No veo por qué no querría usar un interruptor aquí; probablemente sea igual de eficiente, si eso es lo que le preocupa.

+0

Sí, todo se trata de velocidad. ¿Es probable que sea tan rápido si hay 2^16 casos? –

+3

@youllknow: un conmutador debe ser tan rápido como un goto calculado, ya que el compilador también debe crear una tabla de salto – Christoph

+0

Si realmente quiere tener objetivos de salto de 65K, no me sorprendería si la mayoría de los compiladores se cayeran al intentar compilar un interruptor con tantos casos. –

1

Para una respuesta simple, en lugar de obligar a los compiladores a hacer cosas realmente estúpidas, aprenda buenas prácticas de programación.

+7

Sin conocer el contexto, ¿cómo puedes juzgar esto como "cosas realmente estúpidas"? Las reglas para seguir ciegas (como "goto is evil") son buenas para los principiantes. Los programadores experimentados saben dónde hacer una excepción. – lispmachine

+1

Después de considerarlo, es poco probable que un programador experimentado haga tales preguntas, pero es una descortesía prejuzgar. – lispmachine

+2

se utiliza para un tokenizer –

2

Es posible que desee consultar setjmp/longjmp.

1

Tokenizer? Esto se parece a lo que se hizo gperf. No, realmente, échale un vistazo.

1

compiladores optimizadores (incluyendo GCC) compilará una sentencia switch en una tabla de saltos (haciendo una instrucción switch exactamente tan rápido como lo que estamos tratando de construir) si se cumplen las siguientes condiciones:

El conmutador los casos (números de estado) comienzan en cero.

Sus cajas de interruptores están aumentando estrictamente.

No omita ningún número entero en sus cajas de interruptores.

Existen suficientes ejemplos de que una tabla de saltos es más rápido (un par de docenas de comparación y GOTOS en el método de cada uno de los casos de cheques de hacer frente a las sentencias switch es en realidad más rápido que una tabla de saltos.)

Esto tiene la ventaja de permitirle escribir su código en C estándar en lugar de confiar en una extensión de compilación. Funcionará igual de rápido en GCC. También funcionará igual de rápido en la mayoría de los compiladores de optimización (sé que el compilador de Intel lo hace, no estoy seguro de las cosas de Microsoft). Y funcionará, aunque más lento, en cualquier compilador.

+0

Interesante. ¿De dónde sacas las condiciones? –

Cuestiones relacionadas