Quindi mi piacerebbe poter chiamare una funzione come questa:
void func(1, 2, 3, (void*)label) // can return normal or to the labels
//some code
label:
//different code
È possibile, ed è una cattiva pratica?
Quindi mi piacerebbe poter chiamare una funzione come questa:
void func(1, 2, 3, (void*)label) // can return normal or to the labels
//some code
label:
//different code
È possibile, ed è una cattiva pratica?
In C99 o C11 standard non puoi.
Ma GCC ha un'estensione linguistica, etichetta come valori , che potrebbe essere di aiuto (e Clang / LLVM lo accetta anche) .
Anche con il gotos calcolato fornito dall'estensione, saltare in un'altra routine è (quasi) un comportamento indefinito. Puoi saltare indirettamente a un'etichetta all'interno della stessa stessa . Puoi restituire un'etichetta in cui saltare; ovvero, goto *fun(&&label1, &&label2, x, y);
con fun(void*l1, void*l2, int x, int y)
che restituisce uno ( l1
o l2
) dei suoi primi due argomenti. Puoi avere codice threadato .
Si noti che su molti processori, un salto indiretto (o chiamata) è più lento di uno diretto (perché danneggia il precedente di branch ). Quindi non penso che restituire un'etichetta per una percentuale indiretta di% co_de sia una buona pratica (quindi la risposta di John Bode suggerire un goto
è meglio), ma potresti avere strani casi d'angolo.
Sappi anche che il recente GCC (cioè GCC 6 nel giugno 2016) può a volte ottimizza per fare una richiamo .
Leggi anche su stile di passaggio continuo (e anche su continuazioni & continuazioni delimitate , call / cc in Scheme , < a href="https://en.wikipedia.org/wiki/Callback_(computer_programming)"> callback , chiusure e forse loop di eventi ; Prolog e le sue backtracking abilità; coroutine ; iteratori ; discussioni ; eccezioni ; semantica operativa & semantica denotazionale ). Potrebbe ispirarti; guarda all'interno dell'implementazione di Schema di pollo e leggi Cheney sul MTA e il SICP libro. Se stai generando un codice C (ad esempio da un linguaggio simile a Lisp) o vuoi capire come funziona Lisp o Scheme, leggi anche Lisp In Small Pieces book.
Per la gestione delle eccezioni di basso livello in C, longjmp (con switch
) è il modo preferito (si veda anche lo specifico obsoleto, Unix, setcontext (3) ...; controlla backtrace (3) . ..). Ma fa attenzione. Vedi questo & che .
Puoi (vedi le etichette come Valori estensione gcc) . È piuttosto dubbio che dovresti.
Dijkstra ci ha insegnato che goto era dannoso . Stai proponendo non solo l'uso di goto e una tabella di salto, ma anche una funzione che ritorna all'etichetta passata.
Comprendi che una funzione è essenzialmente un goto (salto) con un po 'di spinta sullo stack (il frame dello stack). Quella cornice è pop-up su un ritorno per mettere lo stack in uno stato previsto. Se intendi sovvertire il ritorno della funzione nella sua posizione di chiamata, per andare invece all'etichetta passata, allora dovresti sapere come ripulire lo stack e avere il proprio modo di tornare al blocco principale di codice o alcuni idea di come il programma dovrebbe continuare da qui.
Trucchi come questo sono come funzionano le funzioni in primo luogo. Se lo desideri, puoi progettare il tuo protocollo di funzione in questo modo. La domanda è perché? Le funzioni lo fanno già. Puoi risolvere molti problemi che potrebbero richiedere questo con le funzioni, e se hai bisogno di essere fantasioso, puntatori di funzioni .
Non è possibile, e se lo fosse, sarebbe probabilmente considerato una cattiva pratica 1 . Gotos all'interno di una funzione rende il codice impossibile da tracciare con l'ispezione; le funzioni gotos across potrebbero peggiorare la situazione molto .
Ecco un esempio:
i = 4;
label: printf( "%d\n", i );
Quale valore viene effettivamente stampato? È 4
? È mai 4
? Non posso sapere fino a quando non inseguo ogni istanza di goto label
in quella funzione. Supponiamo che io scopra qualcosa del genere:
i = 2;
goto label;
...
i = 4;
label: printf( "%d\n", i );
In questo scenario, i
è mai 4
; questo incarico viene saltato incondizionatamente. Ora, immagina che questo tipo di cose possa accadere attraverso e il lavoro diventa ancora più brutto.
Puoi gestire la gestione delle eccezioni con l'utilizzo di setjmp
e longjmp
, ma non è esattamente quello che stai cercando. Onestamente, avrei func
restituire un codice di errore e un ramo basato su quello:
switch ( func( 1, 2, 3 ) )
{
case NORMAL:
// normal processing
break;
case THIS_ERROR:
// error processing
break;
case THAT_ERROR:
// error processing
break;
...
}
o
if ( func( 1, 2, 3 ) )
{
// normal processing
}
else
{
// error processing
}