En C/C++, les variables globales sont-elles aussi mauvaises que le pense leur professeur?
Le problème des variables globales est que, chaque fonction y ayant accès, il est de plus en plus difficile de déterminer quelles fonctions lisent et écrivent réellement ces variables.
Pour comprendre le fonctionnement de l'application, vous devez prendre en compte toutes les fonctions qui modifient l'état global. Cela peut être fait, mais à mesure que l’application grandit, elle deviendra de plus en plus difficile au point d’être pratiquement impossible (ou du moins une perte de temps totale).
Si vous ne comptez pas sur des variables globales, vous pouvez transmettre l’état entre différentes fonctions selon vos besoins. De cette façon, vous avez beaucoup plus de chances de comprendre le rôle de chaque fonction, sans avoir à tenir compte de l'état global.
L'important est de se rappeler l'objectif général: la clarté
La règle "pas de variables globales" existe, car la plupart du temps, les variables globales rendent la signification du code moins claire.
Cependant, comme beaucoup de règles, les gens se souviennent de la règle et non de ce que cette règle était censée faire.
J'ai vu des programmes qui semblaient doubler la taille du code en transmettant un nombre énorme de paramètres simplement pour éviter le mal des variables globales. En fin de compte, l’utilisation de globals aurait rendu le programme plus clair visible pour ceux qui le lisaient. En adhérant aveuglément à la Parole de la règle, le programmeur original avait manqué à l’intention de la règle.
Donc, oui, les globals sont souvent mauvais. Mais si vous estimez que l'intention du programmeur est rendue plus claire par l'utilisation de variables globales, poursuivez. Cependant, souvenez-vous de la perte de clarté qui s'ensuit automatiquement lorsque vous forcez quelqu'un à accéder à un deuxième morceau de code (les globals) pour comprendre le fonctionnement du premier morceau.
Mon professeur avait l'habitude de dire quelque chose comme: utiliser des variables globales est acceptable si vous les utilisez correctement. Je ne pense pas avoir réussi à les utiliser correctement, alors je les ai rarement utilisées.
Les variables globales ne doivent être utilisées qu'en l'absence d'alternative. Et oui, cela inclut les singletons. 90% du temps, des variables globales sont introduites pour économiser le coût de la transmission d'un paramètre. Et puis le codage multithreading/tests unitaires/maintenance se produit et vous avez un problème.
Alors oui, dans 90% des cas, les variables globales sont mauvaises. Les exceptions ne seront probablement pas perçues par vous pendant vos années de collège. Une exception à laquelle je peux penser immédiatement est de traiter avec des objets intrinsèquement globaux tels que des tables d’interruption. Des choses comme DB connection semblent être globales, mais non.
Le problème créé par les variables globales pour le programmeur est qu’il élargit la surface couplage entre composants } entre les divers composants qui utilisent les variables globales. Cela signifie que si le nombre de composants utilisant une variable globale augmente, la complexité des interactions peut également augmenter. Ce couplage accru facilite généralement l’injection de défauts dans le système lors de modifications et rend également les défauts plus difficiles à diagnostiquer et à corriger. Ce couplage accru peut également réduire le nombre d’options disponibles lors de modifications et augmenter l’effort requis pour les modifications, car il est souvent nécessaire de suivre les différents modules qui utilisent également la variable globale afin de déterminer les conséquences des modifications.
Le but de encapsulation }, qui est fondamentalement le contraire de l'utilisation de variables globales, est de diminuer le couplage afin de rendre la compréhension et le changement de source plus faciles, plus sûrs et plus facilement testés. Il est beaucoup plus facile d'utiliser tests unitaires lorsque des variables globales ne sont pas utilisées.
Par exemple, si vous avez une simple variable entière globale utilisée comme indicateur énuméré indiquant que divers composants sont utilisés comme machine à états et que vous apportez une modification en ajoutant un nouvel état à un nouveau composant, vous devez suivre toutes les autres composants pour assurer que le changement ne les affectera pas. Un exemple de problème possible serait si une instruction switch
pour tester la valeur de la variable globale d’énumération avec des instructions case
pour chacune des valeurs actuelles est utilisée à divers endroits et il se trouve que certains des instructions switch
n'ont pas de cas default
pour gérer une valeur inattendue pour le global tout d'un coup, vous avez un comportement indéfini en ce qui concerne l'application.
D'autre part, l'utilisation d'une zone de données partagée peut être utilisée pour contenir un ensemble de paramètres globaux qui sont référencés dans toute l'application. Cette approche est souvent utilisée avec des applications intégrées avec de petites empreintes de mémoire.
Lors de l'utilisation de variables globales dans ce type d'applications, la responsabilité de l'écriture dans la zone de données est généralement attribuée à un composant unique. Tous les autres composants voient la zone sous la forme const
et la lisent sans l'écrire. Cette approche limite les problèmes qui peuvent se développer.
Quelques problèmes de variables globales qu'il convient de contourner
Lorsque la source d'une variable globale telle qu'une structure est modifiée, tout ce qui l'utilise doit être recompilé afin que tout ce qui utilise la variable connaisse sa taille réelle et son modèle de mémoire.
Si plusieurs composants peuvent modifier la variable globale, vous pouvez rencontrer des problèmes de données incohérentes dans la variable globale. Avec une application multi-threading, vous aurez probablement besoin d'ajouter une sorte de région de verrouillage ou critique afin de permettre à un seul thread à la fois de modifier la variable globale et lorsqu'un thread modifie la variable, toutes les modifications sont terminées. et engagé avant que d'autres threads puissent interroger la variable ou la modifier.
Le débogage d'une application multithread qui utilise une variable globale peut être plus difficile. Vous pouvez rencontrer (conditions de concurrence) pouvant créer des défauts difficiles à répliquer. Avec plusieurs composants communiquant via une variable globale, en particulier dans une application multithread, il peut être très difficile de savoir quel composant modifie la variable quand et comment il change la variable.
Les conflits de noms peuvent poser problème avec l'utilisation de variables globales. Une variable locale portant le même nom qu'une variable globale peut masquer la variable globale. Vous rencontrez également le problème de la convention de nommage lors de l'utilisation du langage de programmation C. Une solution consiste à diviser le système en sous-systèmes avec les variables globales d'un sous-système particulier commençant par les mêmes trois premières lettres (voir ceci sur résolution des collisions d'espace de nom dans Objective C ). C++ fournit des espaces de noms et avec C, vous pouvez contourner ce problème en créant une structure visible de manière globale dont les membres sont divers éléments de données et des pointeurs vers des données et des fonctions. Ces données sont fournies dans un fichier sous forme statique, et donc uniquement visibles. la structure globalement visible.Dans certains cas, l'intention de l'application d'origine est modifiée afin que les variables globales fournissant l'état d'un seul thread soient modifiées pour permettre l'exécution de plusieurs threads en double. Un exemple serait une application simple conçue pour un utilisateur unique utilisant des variables globales pour l'état, puis une demande de gestion pour ajouter une interface { REST } _ afin de permettre aux applications distantes d'agir en tant qu'utilisateurs virtuels. Vous devez donc maintenant dupliquer les variables globales et leurs informations d'état afin que l'utilisateur individuel ainsi que chacun des utilisateurs virtuels d'applications distantes disposent de leur propre ensemble unique de variables globales.
Utilisation de C++ namespace
et de la technique struct
pour C.
Pour le langage de programmation C++, la directive namespace
est une aide précieuse pour réduire les risques de conflit de noms. namespace
ainsi que class
et les différents mots clés d'accès (private
, protected
et public
) fournissent la plupart des outils dont vous avez besoin pour encapsuler des variables. Cependant, le langage de programmation C ne fournit pas cette directive. Cette publication stackoverflow, Namespaces in C , fournit certaines techniques pour C.
Une technique utile consiste à avoir une seule zone de données résidente en mémoire définie comme une variable - variable - qui a une visibilité globale et qui contient des pointeurs vers les diverses variables et fonctions globales exposées. Les définitions réelles des variables globales se voient attribuer une étendue de fichier à l'aide du mot clé struct
. Si vous utilisez ensuite le mot clé struct
pour indiquer ceux qui sont en lecture seule, le compilateur peut vous aider à appliquer un accès en lecture seule.
L'utilisation de la technique static
peut également encapsuler le global afin qu'il devienne une sorte de package ou de composant qui se trouve être un global. Avec un composant de ce type, il devient plus facile de gérer les changements qui affectent le global et les fonctionnalités utilisant le global.
Cependant, bien que la technique const
ou struct
puisse aider à gérer les conflits de noms, les problèmes sous-jacents de couplage entre composants que l'utilisation de globals introduit notamment dans une application multithread moderne, persistent.
However while namespace
or the struct
technique can help manage name clashes, the underlying problems of inter-component coupling which the use of globals introduces especially in a modern multi-threaded application, still exist.
Oui, mais vous ne supportez pas le coût des variables globales tant que vous n'avez pas cessé de travailler dans le code qui utilise des variables globales et commencez à écrire quelque chose d'autre qui utilise le code qui utilise des variables globales. Mais le coût est toujours là.
En d’autres termes, il s’agit d’un coût indirect à long terme et, en tant que tel, la plupart des gens pensent que ce n’est pas mauvais.
S'il est possible que votre code se retrouve sous révision intensive au cours d'un procès de la Cour suprême, vous voulez éviter les variables globales.
Voir cet article: Le code de l'alcootest Buggy reflète l'importance de l'examen de la source
Il y avait quelques problèmes avec le style du code identifié par les deux études. L'un des stylistiques questions qui concernaient les examinateurs était l'utilisation intensive de non protégé variables globales. Ceci est considéré forme médiocre car elle augmente le risque que l'état du programme le soit devenir incohérent ou que les valeurs sera modifié par inadvertance ou écrasé. Les chercheurs ont également a exprimé certaines inquiétudes à propos de ce fait cette précision décimale n'est pas maintenu de manière constante tout au long du code.
Mec, je parie que ces développeurs souhaitent ne pas avoir utilisé de variables globales!
Je répondrais à cette question par une autre question: Utilisez-vous singletons / Les singletons sont-ils mauvais?
Parce que (presque tous) l'utilisation de singelton est une variable globale glorifiée.
Les variables globales sont aussi mauvaises que vous les créez, pas moins.
Si vous créez un programme entièrement encapsulé, vous pouvez utiliser des globals. Utiliser des globals est un "péché", mais programmer des péchés est philosophique.
Si vous consultez L.in.oleum , vous verrez une langue dont les variables sont uniquement globales. C'est impossible parce que les bibliothèques n'ont pas d'autre choix que d'utiliser des globals.
Cela dit, si vous avez le choix et que vous pouvez ignorer la philosophie du programmeur, les globaux ne sont pas si mauvais.
Gotos non plus, si vous les utilisez correctement.
Le gros "mauvais" problème est que, si vous les utilisez mal, les gens hurlent, le mars lander s'écrase et le monde explose ... ou quelque chose du genre.
Comme quelqu'un l'a dit (je paraphrase) dans un autre fil "Des règles comme celle-ci ne devraient pas être enfreintes, jusqu'à ce que vous en compreniez pleinement les conséquences"
Il arrive que des variables globales soient nécessaires, ou du moins très utiles (Utilisation de rappels définis par le système, par exemple). D'autre part, ils sont également très dangereux pour toutes les raisons qui vous ont été expliquées.
De nombreux aspects de la programmation devraient probablement être laissés aux experts. Parfois, vous avez besoin d'un couteau très tranchant. Mais vous ne pourrez en utiliser qu'un avant que vous soyez prêt ...
Le problème est moins qu’ils sont mauvais, et plus qu’ils sont dangereux. Ils ont leurs propres avantages et inconvénients, et il existe des situations dans lesquelles ils sont soit le plus efficace, soit le seul moyen de mener à bien une tâche particulière. Cependant, ils sont très faciles à utiliser, même si vous prenez des mesures pour les utiliser correctement.
Quelques pros:
Quelques inconvénients:
Notez, si vous voulez, que les deux premiers avantages et les deux premiers inconvénients que j’ai énumérés sont exactement la même chose, mais avec une formulation différente. En effet, les fonctionnalités d'une variable globale peuvent effectivement être utiles, mais les fonctionnalités mêmes qui les rendent utiles sont à l'origine de tous leurs problèmes.
Quelques solutions potentielles à certains des problèmes:
Globals
ou GlobalVars
), ou utilisez une convention de dénomination normalisée pour les variables globales (telles que global_[name]
ou g_module_varNameStyle
(comme indiqué par underscore_d dans les commentaires)). Cela documentera leur utilisation (vous pouvez trouver du code utilisant des variables globales en recherchant le nom d'espace de nom/struct) et minimisera l'impact sur l'espace de nom global.extern
dans l'en-tête associé, afin que leur utilisation puisse être limitée aux unités de compilation devant y accéder. Si votre code repose sur un grand nombre de variables globales, mais que chaque unité de compilation n'a besoin que d'un accès à quelques-unes d'entre elles, vous pouvez envisager de les trier dans plusieurs fichiers source afin de limiter l'accès de chaque fichier aux variables globales.Qu'ils soient bons ou mauvais dépend de la façon dont vous les utilisez. La majorité a tendance à les mal utiliser, d’où la méfiance générale à leur égard. S'ils sont utilisés correctement, ils peuvent constituer un avantage majeur. cependant, s'ils sont mal utilisés, ils peuvent et vont revenir vous mordre quand et comment vous vous y attendez le moins.
Une bonne façon de voir les choses est qu'elles ne sont pas mauvaises en elles-mêmes, mais elles permettent une mauvaise conception et peuvent multiplier les effets d'une mauvaise conception de manière exponentielle.
Même si vous n'avez pas l'intention de les utiliser, il vaut mieux savoir comment les utiliser en toute sécurité et choisir de ne pas les utiliser que de ne pas les utiliser car vous ne savez pas comment les utiliser en toute sécurité. Si vous vous trouvez dans une situation où vous devez conserver un code préexistant qui repose sur des variables globales, vous risquez d’être confronté à des difficultés si vous ne savez pas comment les utiliser correctement.
Les variables globales sont généralement mauvaises, en particulier si d'autres personnes travaillent sur le même code et ne veulent pas passer 20 minutes à rechercher tous les endroits où la variable est référencée. Et ajouter des threads qui modifient les variables apporte un tout nouveau niveau de maux de tête.
Les constantes globales dans un espace de noms anonyme utilisé dans une seule unité de traduction sont utiles et omniprésentes dans les applications et les bibliothèques professionnelles. Mais si les données sont modifiables et/ou si elles doivent être partagées entre plusieurs TU, vous pouvez les encapsuler. Si ce n'est pas pour des raisons de conception, alors pour le plaisir de déboguer ou de travailler avec votre code.
Utiliser des variables globales est un peu comme balayer la poussière sous un tapis. C'est une solution rapide, et beaucoup plus facile à court terme que d'avoir un aspirateur ou un aspirateur pour le nettoyer. Cependant, si vous finissez par déplacer le tapis plus tard, vous aurez un gros désordre surprise en dessous.
Les variables globales sont mauvaises si elles vous permettent de manipuler des aspects d'un programme qui ne doivent être modifiés que localement. Dans OOP, les éléments globaux entrent souvent en conflit avec l'idée d'encapsulation.
Absolument pas. Les abuser cependant… c'est mauvais.
Les enlever aveuglément pour le plaisir de ne faire que ça ... stupide. Si vous ne connaissez pas les avantages et les inconvénients, il est préférable de rester clair et de suivre les instructions données, mais rien n’implique implicitement les variables globales. Lorsque vous comprenez les avantages et les inconvénients, prenez votre décision.
Je pense que votre professeur essaie de mettre fin à une mauvaise habitude avant même qu'elle ne commence.
Les variables globales ont leur place et, comme beaucoup de gens le disent, savoir où et quand les utiliser peut être compliqué. Donc, je pense que plutôt que de rentrer dans le vif du sujet, pourquoi, comment, quand et où des variables globales, votre professeur a décidé de simplement interdire. Qui sait, il pourrait les interdire à l'avenir.
Les variables globales conviennent pour les petits programmes, mais horribles si elles sont utilisées de la même manière dans les grands.
Cela signifie que vous pouvez facilement prendre l'habitude de les utiliser tout en apprenant. C'est ce que votre professeur essaie de vous protéger.
Quand vous aurez plus d'expérience, il sera plus facile d'apprendre quand tout ira bien.
Oui, parce que si vous laissez des programmeurs incompétents les utiliser (lire 90%, en particulier des scientifiques), vous vous retrouvez avec plus de 600 variables globales réparties sur plus de 20 fichiers et un projet de 12 000 lignes où 80% des fonctions sont annulées, retournées et fonctionnelles entièrement sur l'état global.
Il devient rapidement impossible de comprendre ce qui se passe à un moment donné, à moins de connaître le projet dans son ensemble.
Non, ils ne sont pas mal du tout. Vous devez examiner le code (machine) produit par le compilateur pour prendre cette décision. Parfois, il est bien pire d'utiliser un code local qu'un code global. Notez également que mettre "statique" sur une variable locale en fait une globale (et crée d’autres problèmes laids qu’un vrai global pourrait résoudre). "globals locaux" sont particulièrement mauvais.
Les globales vous permettent également de contrôler parfaitement votre utilisation de la mémoire, ce qui est beaucoup plus difficile à faire avec les habitants. Ces jours-ci ne comptent que dans les environnements embarqués où la mémoire est assez limitée. Ce que vous devez savoir avant de supposer que l’intégration est identique à celle des autres environnements et que les règles de programmation sont les mêmes dans tous les domaines.
Il est bon que vous remettiez en question les règles enseignées. La plupart d’entre elles ne sont pas motivées par les raisons. La leçon la plus importante n’est cependant pas qu’il s’agisse d’une règle à emporter pour toujours, mais d’une règle à respecter pour réussir ce cours et aller de l’avant. Dans la vie, vous constaterez que pour la société XYZ, vous aurez d’autres règles de programmation que vous devrez respecter à la fin pour pouvoir continuer à recevoir un chèque de règlement. Dans les deux cas, vous pouvez faire valoir la règle, mais je pense que vous aurez beaucoup plus de chance au travail qu’à l’école. Vous êtes juste un autre parmi de nombreux étudiants, votre siège sera bientôt remplacé, pas le professeur habituel, dans un poste, vous faites partie d'une petite équipe de joueurs qui doivent voir ce produit jusqu'au bout et dans cet environnement, les règles développées s'appliquent au les membres de l’équipe, ainsi que le produit et la société, donc si tout le monde est d'accord ou si, pour un produit en particulier, il existe une bonne raison technique de violer quelque chose que vous avez appris à l'université ou dans un livre sur la programmation générique, vendez votre idée à l'équipe et écrivez-le comme une méthode valide, sinon la méthode préférée. Tout est juste jeu dans le monde réel.
Si vous suivez toutes les règles de programmation enseignées à l'école ou dans les livres, votre carrière en programmation sera extrêmement limitée. Vous pouvez probablement survivre et mener une carrière fructueuse, mais l’ampleur et la largeur des environnements disponibles seront extrêmement limitées. Si vous savez comment et pourquoi la règle est là et peut la défendre, c’est bien, si vous raisonnez, c’est "parce que mon professeur l’a dit", eh bien ce n’est pas si bon.
Notez que des sujets comme celui-ci sont souvent débattus sur le lieu de travail et continueront de l'être, à mesure que les compilateurs et les processeurs (et les langues) évoluent, de même que ce genre de règles Avance.
Pendant ce temps, faites ce que dit celui qui parle le plus fort ou porte le plus gros bâton (jusqu’à ce que vous soyez celui qui crie le plus fort et porte le plus gros bâton).
L'utilisation de variables globales dépend en fait des exigences. Son avantage est que cela réduit la surcharge liée à la transmission répétée des valeurs.
Mais votre professeur a raison, car cela soulève problèmes de sécurité. Il faut donc éviter autant que possible d'utiliser des variables globales. Les variables globales créent également des problèmes qui sont parfois difficiles à résoudre.
Par exemple:-
Situations dans lesquelles les valeurs des variables obtiennent modifié sur exécution. A ce moment, il est difficile d'identifier quelle partie du code le modifie et à quelles conditions.
Je voudrais argumenter contre l'idée que tout au long de ce fil rend le multi-threading plus difficile ou impossible en soi. Les variables globales ont un état partagé, mais les alternatives aux globales (par exemple, le passage de pointeurs) peuvent également partager un état. Le problème avec le multi-threading est de savoir comment utiliser correctement l'état partagé, et non pas si cet état est partagé par le biais d'une variable globale ou autre.
La plupart du temps, lorsque vous faites du multi-threading, vous devez partager quelque chose. Dans un modèle producteur-consommateur, par exemple, vous pouvez partager une file d'attente sécurisée pour les threads contenant les unités de travail. Et vous êtes autorisé à le partager car cette structure de données est thread-safe. Que cette file d'attente soit globale ou non n'a aucune importance pour la sécurité des threads.
L’espoir implicite exprimé tout au long de ce fil de discussion qu’il est naïf de transformer un programme mono-thread en multi-thread est plus facile. Oui, les globales permettent de se tirer une balle dans le pied plus facilement, mais il y a beaucoup de façons de se tirer une balle.
Je ne préconise pas les globaux, les autres points étant toujours valables, mon point est simplement que le nombre de threads dans un programme n'a rien à voir avec une portée variable.
J'utilise généralement des valeurs globales pour des valeurs qui sont rarement modifiées, comme des singletons ou des pointeurs de fonction vers des fonctions dans une bibliothèque chargée dynamiquement. L'utilisation de globals mutables dans des applications multithreads conduit souvent à un bogue difficile à suivre, j'essaie donc de l'éviter en règle générale.
Utiliser un global au lieu de passer un argument est souvent plus rapide, mais si vous écrivez une application multithread, ce que vous faites souvent de nos jours, cela ne fonctionne généralement pas très bien (vous pouvez utiliser la statique-thread mais le gain de performance est discutable) .
À la fin de la journée, votre programme ou votre application peut toujours fonctionner, mais il s’agit simplement d’être bien rangé et d’avoir une compréhension complète de ce qui se passe. Si vous partagez une valeur variable entre toutes les fonctions, il peut s'avérer difficile de déterminer quelle fonction modifie la valeur (si la fonction le fait) et rend le débogage un million de fois plus difficile.
Global sont bons quand il s’agit de configuration . Lorsque nous voulons que notreconfiguration/changesait un impact global sur le projet entier.
Ainsi, nous pouvons changer une configuration et les modifications sont dirigées vers le projet entier. Mais je dois vous avertir que vous devez être très intelligent pour utiliser les globals.
Tôt ou tard, vous devrez modifier le mode de définition de cette variable ou ce qui se passe lorsque vous y accédez, ou vous devez simplement rechercher où elle a été modifiée.
Il est pratiquement toujours préférable de ne pas avoir de variables globales. Il suffit d’écrire la méthode d’obtention et d’établissement du barrage et d’être gland lorsque vous en avez besoin un jour, une semaine ou un mois plus tard.
Dans une application Web, les applications Web peuvent être utilisées pour conserver des données de session/fenêtre/thread/utilisateur sur le serveur pour des raisons d'optimisation et de protection contre les pertes de travail lorsque la connexion est instable. Comme mentionné, les conditions de course doivent être gérées. Nous utilisons une seule instance d'une classe pour ces informations, qui sont gérées avec soin.
la sécurité est moins importante signifie que n'importe qui peut manipuler les variables si elles sont déclarées globales. Pour celui-ci, prenez cet exemple si vous avez la balance comme variable globale dans votre programme bancaire, la fonction utilisateur peut manipuler ceci ainsi que le responsable de banque pouvant également manipuler Cela pose donc un problème. Seul un utilisateur devrait avoir la fonction de lecture seule et de retrait, mais le greffier de la banque peut ajouter le montant lorsque l’utilisateur remet personnellement l’argent se trouvant sur le bureau. C’est ainsi que cela fonctionne.