2009-06-02 10 views
17

Je veux déclarer un tableau de "jumplabels". Puis je veux sauter à un "jumplabel" dans ce tableau.Comment stocker les étiquettes goto dans un tableau, puis passer à eux?

Mais je n'ai aucune idée de comment faire cela.

Il devrait ressembler le code suivant:

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

Quelqu'un at-il une idée comment réaliser cela?

+3

Si c'est un devoir, je ne vous recommande pas d'aller de cette façon - votre professeur ne l'aimerait pas. – qrdl

+2

non ce n'est pas, j'ai dix-sept ans et j'essaie d'écrire un tokenizer simple et rapide. J'ai pensé à l'accélérer de cette façon. –

+2

Vous mentionnez dans les commentaires ci-dessous qu'il peut y avoir 2 ** 16 cas. Est-ce que ce sera la norme? Si c'est le cas, cela change radicalement le problème. –

Répondre

38

Il est possible avec la fonction GCC connue sous le nom "labels as values".

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

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

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

Fonctionne uniquement avec GCC!

+0

merci! c'est exactement ce que je voulais savoir! –

+0

si c'est la bonne réponse, marquez-le comme une réponse! –

+3

+1 pour exposer bizarrement (blech, gack, beurk!) –

11

Il n'y a aucun moyen direct de stocker les adresses de code pour passer en C. Que diriez-vous d'utiliser le commutateur.

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

Vous pouvez trouver un code similaire produit par chaque analyseur de machine d'état sans pile. Un tel code n'est pas facile à suivre, à moins que ce soit du code généré ou que votre problème soit le plus facilement décrit par une machine d'état, je vous recommande de ne pas le faire.

+1

Je pense que vous voulez laisser tomber 'goto' devant l'étiquette' jump_target' – Christoph

+0

droite, corrigé maintenant – lispmachine

8

pourriez-vous utiliser des pointeurs de fonction au lieu de goto?

De cette façon, vous pouvez créer une série de fonctions à appeler et appeler la fonction appropriée.

+0

Je sais qu'il peut me faire avec des pointeurs de fonction. Mais ce serait lent, parce que je devrais appeler une fonction à souvent. Je pense que l'appel serait trop gros! –

+7

@youllknow: Les mots "je pense" dans le commentaire ci-dessus me disent que vous êtes en danger réel de tomber pour "optimisation prématurée". Le premier objectif devrait être de commencer par une solution «de travail» claire, puis de l'optimiser si nécessaire. Considérez ceci: seulement 1 compilateur a cette fonctionnalité comme une extension, cependant, chaque compilateur C/C++ utilise des machines à états. Si c'est la meilleure façon de résoudre ce problème, pourquoi chaque compilateur n'a-t-il pas cette fonctionnalité? –

+0

@Richard Corden: Donc vous pensez que l'amélioration de la vitesse est très faible? J'ai aussi pensé au tableau de pointeurs de fonction. Le problème est que les fonctions seraient appelées très souvent, mais elles ne font que de petites choses. Donc, je pensais que l'appel de la fonction pourrait être plus cher que ce que fait la fonction. Je suis capable d'implémenter mon problème avec des pointeurs de fonction, mais j'ai pensé que je pouvais l'accélérer avec la "méthode goto". Quelle est votre opionion? –

6

En standard C standard, ce n'est pas possible pour autant que je sache. Il existe cependant une extension dans le compilateur GCC, documented here, qui rend cela possible. L'extension introduit le nouvel opérateur &&, pour prendre l'adresse d'une étiquette, qui peut ensuite être utilisée avec l'instruction goto.

+0

Intéressant, je ne le savais pas. Merci. –

+0

oui, très bien!, Merci! –

16

goto nécessite une étiquette de compilation. À partir de cet exemple, il semble que vous implémentez une sorte de machine à états. Le plus souvent ils sont mis en œuvre en tant que construction switch-case:

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

    /* ... */ 
} 

Si vous avez besoin d'être plus dynamique, utilisez un tableau de pointeurs de fonction.

+0

À noter que 'switch' est pour les expressions de cas compact est mis en œuvre avec une table de saut qui est fondamentalement la même chose qu'un tableau ou des étiquettes: http://stackoverflow.com/questions/14067547/how-switch-case-statement-implemented -or-works-internal –

+0

+1 pour "utiliser un tableau de pointeurs de fonction". ou de nos jours, un tableau de 'std :: function' stockant lambdas pourrait fournir une syntaxe beaucoup plus compacte. –

5

C'est ce que les instructions switch sont pour.

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

Notez qu'il n'est pas nécessairement traduit dans une table de saut par le compilateur.

Si vous voulez vraiment construire la table de sauts vous-même, vous pouvez utiliser un tableau de pointeurs de fonction.

+0

C'était seulement un exemple simple ... Dans mon option le commutateur est de ralentir si j'ai 2^16 cas? N'est-ce pas? –

+3

@youllknow: souvent de bons compilateurs optimisent un switch dense dans une table de saut pour vous. Donc non, les commutateurs ne sont pas nécessairement lents. – user83255

2

Vous ne pouvez pas le faire avec un goto - les étiquettes doivent être des identificateurs, non variables ou constantes. Je ne vois pas pourquoi vous ne voudriez pas utiliser un interrupteur ici - ce sera probablement aussi efficace, si c'est ce qui vous concerne.

+0

Oui, tout est une question de vitesse! Est-il encore probable que si il y a 2^16 cas? –

+3

@youllknow: un commutateur devrait être aussi rapide qu'un goto calculé, car le compilateur devrait également créer une table de sauts – Christoph

+0

Si vous voulez vraiment avoir des cibles de saut de 65K, je ne serais pas surpris si la plupart des compilateurs tombaient en essayant de compiler un commutateur avec autant de cas. –

1

Pour une réponse simple, au lieu de forcer les compilateurs à faire des choses stupides réel, apprendre les bonnes pratiques de programmation.

+7

Sans connaître le contexte, comment pouvez-vous juger cela comme "truc vraiment stupide"? Suivre aveuglément les règles (comme "goto is evil") est bon pour les débutants. Les programmeurs expérimentés savent où faire une exception. – lispmachine

+1

Après examen, il est peu probable qu'un programmeur expérimenté pose une telle question, mais il est impoli de préjuger. – lispmachine

+2

il est utilisé pour un tokenizer –

2

Vous pouvez regarder setjmp/longjmp.

1

Tokenizer? Cela ressemble à ce que gperf a été fait pour. Non vraiment, jetez y un coup d'oeil.

1

compilateurs Optimisation (y compris GCC) compileront une instruction switch dans une table de saut (faire une instruction switch exactement aussi vite que la chose que vous essayez de construire) si les conditions suivantes sont réunies:

Votre commutateur cas (numéros d'état) commencent à zéro.

Vos boîtiers de commutateur sont en augmentation stricte.

Vous ne sautez aucun entier dans vos commutateurs.

Il y a suffisamment de cas qu'une table de saut est plus rapide (une douzaine de couple compare-et-GOTO dans la méthode de contrôle-chaque cas de traitement des déclarations de commutation est plus rapide qu'une table de saut.)

Cela a l'avantage de vous permettre d'écrire votre code en C standard au lieu de s'appuyer sur une extension de compilateur. Cela fonctionnera aussi rapidement dans GCC. Il fonctionnera aussi rapidement dans la plupart des compilateurs optimisateurs (je sais que le compilateur Intel le fait, pas sûr de Microsoft). Et cela fonctionnera, bien que plus lentement, sur n'importe quel compilateur.

+0

Intéressant. D'où prenez-vous les conditions? –