Contexte historique : Rappelons-nous que Dijkstra a écrit Goto Considered Harmful en 1968, quand beaucoup de programmeurs utilisaient goto
en remplacement de la programmation structurée (if
, while
, for
, etc.).
C'est 44 ans plus tard, et il est rare de trouver cette utilisation de goto
dans la nature. La programmation structurée a déjà gagné, il y a longtemps.
Analyse de cas :
L'exemple de code ressemble à ceci :
SETUP...
again:
COMPUTE SOME VALUES...
if (cmpxchg64(ptr, old_val, val) != old_val)
goto again;
La version structurée ressemble à ceci :
SETUP...
do {
COMPUTE SOME VALUES...
} while (cmpxchg64(ptr, old_val, val) != old_val);
Quand je regarde la version structurée, je pense immédiatement, "c'est une boucle". Quand je regarde le goto
version, je la considère comme une ligne droite avec un cas "réessayer" à la fin.
Le goto
la version a à la fois SETUP
et COMPUTE SOME VALUES
sur la même colonne, ce qui souligne que la plupart du temps, le flux de contrôle passe par les deux. La version structurée met SETUP
et COMPUTE SOME VALUES
sur différentes colonnes, ce qui souligne que le contrôle peut les traverser différemment.
La question ici est de savoir quel type d'accent voulez-vous mettre dans le code ? Vous pouvez comparer cela avec goto
pour la gestion des erreurs :
Version structurée :
if (do_something() != ERR) {
if (do_something2() != ERR) {
if (do_something3() != ERR) {
if (do_something4() != ERR) {
...
Aller à la version :
if (do_something() == ERR) // Straight line
goto error; // |
if (do_something2() == ERR) // |
goto error; // |
if (do_something3() == ERR) // |
goto error; // V
if (do_something4() == ERR) // emphasizes normal control flow
goto error;
Le code généré est fondamentalement le même, nous pouvons donc le considérer comme un problème typographique, comme l'indentation.
Très bonne question, et je pense que seul(s) l'auteur(s) peut fournir une réponse définitive. J'ajouterai ma petite spéculation en disant qu'il aurait pu commencer par l'utiliser pour la gestion des erreurs, comme l'a expliqué @Izkata et que les portes étaient alors ouvertes pour l'utiliser également pour les boucles de base.
L'utilisation de la gestion des erreurs est légitime dans la programmation système, à mon avis. Une fonction alloue progressivement de la mémoire au fur et à mesure de sa progression, et si une erreur est rencontrée, elle sera goto
l'étiquette appropriée pour libérer les ressources dans l'ordre inverse à partir de ce point.
Ainsi, si l'erreur se produit après la première allocation, elle passera à la dernière étiquette d'erreur, pour ne libérer qu'une seule ressource. De même, si l'erreur se produit après la dernière allocation, elle sautera au premier libellé d'erreur et s'exécutera à partir de là, libérant toutes les ressources jusqu'à la fin de la fonction. Un tel modèle de gestion des erreurs doit encore être utilisé avec précaution, en particulier lors de la modification du code, valgrind et les tests unitaires sont fortement recommandés. Mais il est sans doute plus lisible et maintenable que les approches alternatives.
Une règle d'or pour utiliser goto
est d'éviter le soi-disant code spaghetti. Essayez de tracer des lignes entre chaque goto
déclaration et son étiquette respective. Si vous avez des lignes qui se croisent, eh bien, vous avez franchi une ligne :). Une telle utilisation de goto
est très difficile à lire et une source courante de bogues difficiles à suivre, car ils se trouveraient dans des langages comme BASIC qui en dépendaient pour le contrôle de flux.
Si vous ne faites qu'une seule boucle simple, vous n'obtiendrez pas de lignes de franchissement, il est donc toujours lisible et maintenable, devenant en grande partie une question de style. Cela dit, comme ils peuvent tout aussi facilement être réalisés avec les mots-clés de boucle fournis par la langue, comme vous l'avez indiqué dans votre question, ma recommandation serait toujours d'éviter d'utiliser goto
pour les boucles, simplement parce que le for
, do/while
ou while
les constructions sont plus élégantes de par leur conception.
Dans le cas de cet exemple, je soupçonne qu'il s'agissait de mettre à niveau le support SMP dans du code qui a été écrit à l'origine d'une manière non sécurisée pour SMP. Ajouter un goto again;
est beaucoup plus simple et moins invasif que de restructurer la fonction.
Je ne peux pas dire que j'aime beaucoup ce style, mais je pense aussi qu'il est malavisé d'éviter goto
pour des raisons idéologiques. Un cas particulier de goto
l'utilisation (différente de cet exemple) est où goto
sert uniquement à avancer dans une fonction, jamais à reculer. Cette classe d'utilisations n'entraîne jamais de constructions de boucle issues de goto
, et c'est presque toujours le moyen le plus simple et le plus clair d'implémenter le comportement nécessaire (qui consiste généralement à nettoyer et à revenir en cas d'erreur).