2009-06-02 7 views
17

Voglio dichiarare un array di "jumplabels".Come memorizzare le etichette goto in una matrice e poi saltare a loro?

Quindi voglio passare a una "jumplabel" in questo array.

Ma non ho idea di come farlo.

Esso dovrebbe essere simile al seguente codice:

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

Qualcuno ha un'idea di come eseguire questa?

+3

Se si tratta di un compito a casa, non ti consiglio di andare in questo modo - al tuo professore non piacerebbe. – qrdl

+2

no non lo è, ho diciassette anni e sto provando a scrivere un semplice tokenizzatore veloce. Ho pensato di accelerare in questo modo. –

+2

Nei commenti in basso si menziona che potrebbero esserci 2 ** 16 casi. Sarà questa la norma? Se è così, cambia drasticamente il problema. –

risposta

38

È possibile con la funzione GCC nota come "labels as values".

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

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

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

Funziona solo con GCC!

+0

grazie! questo è esattamente quello che volevo sapere! –

+0

se questa è la risposta giusta, contrassegnala come risposta! –

+3

+1 per mostrare stranezze (blech, gack, yuck!) –

11

Non esiste un modo diretto per memorizzare gli indirizzi di codice in cui passare in C. Come utilizzare lo switch.

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

È possibile trovare codice simile prodotto da ogni parser/generatore di stato senza stack. Tale codice non è facile da seguire, a meno che non sia generato codice o il tuo problema è il più facilmente descritto dalla macchina a stati, consiglierei di non farlo.

+1

Penso che vogliate rilasciare il 'goto' davanti all'etichetta' jump_target' – Christoph

+0

a destra, ora fisso – lispmachine

8

potresti utilizzare i puntatori di funzione invece di goto?

In questo modo è possibile creare una serie di funzioni per chiamare e chiamare quella appropriata.

+0

So che può essere fatto con i puntatori di funzione. Ma questo sarebbe lento, perché dovrei chiamare una funzione spesso. Penso che il call-overhead sarebbe troppo grande! –

+7

@youllknow: Le parole "I think" nel commento sopra mi dicono che sei in serio pericolo di cadere per "ottimizzazione prematura". Il primo obiettivo dovrebbe essere quello di iniziare con una chiara soluzione "funzionante" e quindi ottimizzarla secondo necessità. Considera questo: solo 1 compilatore ha questa funzionalità come estensione, tuttavia, ogni compilatore C/C++ utilizza macchine a stati. Se questo è il modo migliore per risolvere questo problema, perché non tutti i compilatori hanno questa caratteristica? –

+0

@Richard Corden: Quindi pensi che il miglioramento della velocità sia molto basso? Ho pensato anche alla funzione pointer array. Il problema è che le funzioni verrebbero chiamate molto spesso, ma fanno solo piccole cose. Quindi pensavo che chiamare la funzione potesse essere più costoso di quello che fa la funzione. Sono in grado di implementare il mio problema con i puntatori di funzione, ma ho pensato di accelerarlo con il "metodo goto". Qual è il tuo parere? –

6

In semplice standard C, questo non è possibile per quanto ne so. Esiste tuttavia un'estensione nel compilatore GCC, documented here, che rende ciò possibile.

L'estensione introduce il nuovo operatore &&, per prendere l'indirizzo di un'etichetta, che può quindi essere utilizzato con l'istruzione goto.

+0

Interessante, non lo sapevo. Grazie. –

+0

sì, molto bello !, grazie! –

16

goto ha bisogno di un'etichetta in fase di compilazione.

Da questo esempio sembra che si stia implementando una sorta di macchina a stati. Più comunemente la loro applicazione come un interruttore-costrutto case:

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

    /* ... */ 
} 

Se avete bisogno di essere più dinamico, utilizzare un array di puntatori a funzione.

+0

Vale la pena notare che 'switch' è per le espressioni di case compatte implementate con una tabella di salto che è fondamentalmente la stessa di una matrice o di etichette: http://stackoverflow.com/questions/14067547/how-switch-case-statement-implemented -o-funziona-internamente –

+0

+1 per "usa un array di puntatori di funzione". o al giorno d'oggi, un array di 'std :: function' che memorizza lambda potrebbe fornire una sintassi molto più compatta. –

5

Ecco a cosa servono le dichiarazioni switch.

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

Nota che non è necessariamente tradotto in una tabella di salto dal compilatore.

Se si desidera creare autonomamente il jump table, è possibile utilizzare un array di puntatori di funzioni.

+0

Era solo un semplice esempio ... Nella mia opzione l'opzione è di rallentare se ho 2^16 casi? Non è vero? –

+3

@youllknow: spesso i buoni compilatori ottimizzano uno switch denso in un jump table per te. Quindi no, gli interruttori non sono necessariamente lenti. – user83255

2

Non è possibile farlo con un goto: le etichette devono essere identificatori, non variabili o costanti. Non riesco a capire perché non vorresti usare un interruttore qui - probabilmente sarà altrettanto efficiente, se questo è ciò che ti riguarda.

+0

Sì, è tutto sulla velocità! È anche probabile che altrettanto veloce se ci sono 2^16 casi? –

+3

@youllknow: un interruttore dovrebbe essere veloce come un goto calcolato, in quanto il compilatore dovrebbe creare anche una tabella di salto – Christoph

+0

Se si vuole davvero avere obiettivi di salto di 65K non sarei sorpreso se la maggior parte dei compilatori cadrà cercando di compilare un interruttore con tanti casi. –

1

Per una risposta semplice, invece di forzare i compilatori a fare cose veramente stupide, apprendere buone pratiche di programmazione.

+7

Senza conoscere il contesto, come puoi giudicare questo come "roba stupida"? Le seguenti regole ciecamente (come "goto is evil") sono buone per i principianti. I programmatori esperti sanno dove fare un'eccezione. – lispmachine

+1

Dopo l'esame è improbabile che un programmatore esperto possa fare questa domanda, ma questo è scortese da pregiudicare. – lispmachine

+2

è usato per un tokenizer –

2

Si potrebbe voler dare un'occhiata a setjmp/longjmp.

1

Tokenizer? Questo sembra quello per cui è stato creato gperf. No davvero, dai un'occhiata.

1

compilatori Ottimizzazione (tra cui GCC) saranno compilare un'istruzione switch in una tabella di salto (fare una dichiarazione interruttore esattamente veloce come la cosa si sta cercando di costruire) se sono soddisfatte le seguenti condizioni:

tuo interruttore i casi (numeri di stato) iniziano da zero.

Le custodie degli interruttori sono in costante aumento.

Non si salta alcun numero intero nelle proprie custodie.

ci sono abbastanza casi che una tabella salto è effettivamente più veloce (un paio di dozzine confrontare-e-goto nel metodo di controllo-ogni-caso di trattare con istruzioni switch è in realtà più veloce di un tavolo salto.)

Questo ha il vantaggio di permetterti di scrivere il tuo codice in C standard invece di basarti su un'estensione del compilatore. Funzionerà altrettanto velocemente in GCC. Funzionerà altrettanto velocemente nella maggior parte dei compilatori ottimizzanti (so che il compilatore Intel lo fa, non sono sicuro delle cose di Microsoft). E funzionerà, anche se più lentamente, su qualsiasi compilatore.

+0

Interessante. Da dove prendi le condizioni? –

Problemi correlati