Apparemment, il existe une grande variété d'opinions, allant de " Jamais! Toujours encapsuler (même si c'est avec une simple macro!) " à " Ce n'est pas grave, utilisez-les quand c'est plus pratique que ne pas. "
Alors.
Des raisons spécifiques et concrètes (de préférence avec un exemple)
Bien que cela soit subjectif, je choisirai une réponse (celle qui représente le mieux la relation amour/haine que chaque développeur devrait avoir avec les globals) et la communauté votera pour la leur juste en dessous.
Je pense qu'il est important que les débutants aient ce type de référence, mais s'il vous plaît, ne l'encombrez pas s'il existe une autre réponse très similaire au vôtre - ajoutez un commentaire ou modifiez la réponse de quelqu'un d'autre.
-Adam
Les variables doivent toujours avoir la plus petite portée possible. L'argument derrière cela est que chaque fois que vous augmentez la portée, vous avez plus de code qui modifie potentiellement la variable, ainsi plus de complexité est induite dans la solution.
Il est donc clair qu’il est préférable d’éviter d’utiliser des variables globales si la conception et la mise en œuvre le permettent naturellement. Pour cette raison, je préfère ne pas utiliser de variables globales sauf si elles sont vraiment nécessaires.
Je ne peux pas non plus être d'accord avec l'énoncé "jamais". Comme tout autre concept, les variables globales sont un outil à utiliser en cas de besoin. Je préférerais utiliser des variables globales plutôt que des constructions artificielles (telles que le transfert de pointeurs) qui ne masqueraient que l'intention réelle . Les bons exemples d'utilisation de variables globales sont les implémentations de modèle singleton ou l'accès aux registres dans des systèmes embarqués.
Comment détecter réellement les utilisations excessives de variables globales: inspection, inspection, inspection. Chaque fois que je vois une variable globale, je dois me demander: est-ce vraiment nécessaire à l'échelle mondiale?
La seule façon de faire fonctionner les variables globales est de leur donner des noms qui garantissent qu’elles sont uniques.
Ce nom a généralement un préfixe associé à un certain "module" ou une collection de fonctions pour lesquelles la variable globale est particulièrement ciblée ou significative.
Cela signifie que la variable "appartient" à ces fonctions - cela en fait partie. En effet, le global peut généralement être "encapsulé" avec une petite fonction qui va de pair avec les autres fonctions - dans le même préfixe du même nom de fichier .h
.
Prime.
Lorsque vous faites cela, tout à coup, ce n'est plus vraiment global. Cela fait maintenant partie d'un module de fonctions connexes.
Cela peut toujours être fait. Avec un peu de réflexion, chaque variable précédemment globale peut être affectée à un ensemble de fonctions, affectée à un fichier .h
spécifique et isolée avec des fonctions vous permettant de modifier la variable sans rien perdre.
Plutôt que de dire "n'utilisez jamais de variables globales", vous pouvez dire "attribuer les responsabilités de la variable globale à un module qui en a le plus de sens".
Considérez ce koan: "si la portée est suffisamment étroite, tout est global".
À cet âge, il est toujours possible d’avoir besoin d’écrire un programme utilitaire très rapide pour effectuer un travail ponctuel.
Dans de tels cas, l'énergie nécessaire pour créer un accès sécurisé aux variables est supérieure à l'énergie économisée par les problèmes de débogage dans un utilitaire aussi petit.
C’est le seul cas auquel je puisse penser immédiatement où les variables globales sont judicieuses et relativement rare. Des programmes nouveaux et utiles, si petits qu'ils peuvent être complètement conservés dans la mémoire à court terme du cerveau, sont de moins en moins fréquents, mais ils existent toujours.
En fait, je pourrais affirmer hardiment que si le programme n’est pas si petit, les variables globales devraient être illégales.
Les variables globales en C sont utiles pour rendre le code plus lisible si une variable est requise par plusieurs méthodes (au lieu de la transmettre à chaque méthode). Cependant, ils sont dangereux car tous les emplacements ont la possibilité de modifier cette variable, ce qui rend potentiellement difficile la traçabilité des bogues. Si vous devez utiliser une variable globale, assurez-vous toujours qu'elle est uniquement modifiée directement par une méthode et que tous les autres appelants utilisent cette méthode. Cela facilitera beaucoup le débogage des problèmes liés aux modifications de cette variable.
Lorsque le code thread-safe ne vous inquiète pas : utilisez-les partout où cela fait sens, c'est-à-dire qu'il est logique d'exprimer quelque chose en tant qu'état global.
Lorsque votre code est multi-threadé : à éviter à tout prix. Les variables globales abstraites dans des files d'attente de travail ou dans une autre structure thread-safe, ou si c'est absolument nécessaire, mettez-les dans des verrous, en gardant à l'esprit qu'il s'agit probablement de goulots d'étranglement dans le programme.
Je venais du camp "jamais", jusqu'à ce que je commence à travailler dans l'industrie de la défense. Certaines normes de l'industrie exigent que les logiciels utilisent des variables globales au lieu d'une mémoire dynamique (malloc dans le cas C). Je dois repenser mon approche en matière d'allocation de mémoire dynamique pour certains des projets sur lesquels je travaille. Si vous pouvez protéger la mémoire "globale" avec les sémaphores, threads, etc. appropriés, cette approche peut être acceptable pour la gestion de votre mémoire.
La complexité du code n'est pas la seule optimisation du problème. Pour de nombreuses applications, l’optimisation des performances a une priorité bien plus grande. Mais plus important encore, l’utilisation de variables globales peut considérablement réduire la complexité du code dans de nombreuses situations. Il existe de nombreuses situations, peut-être spécialisées, dans lesquelles les variables globales sont non seulement une solution acceptable, mais également une solution préférée. Mon exemple spécialisé préféré est leur utilisation pour assurer la communication entre le thread principal d'une application avec une fonction de rappel audio s'exécutant dans un thread en temps réel.
Il est trompeur de suggérer que les variables globales constituent un passif dans les applications multithreads, car TOUTES les variables, quelle que soit leur portée, constituent un passif potentiel si elles sont exposées à des modifications sur plusieurs threads.
Utilisez les variables globales avec parcimonie. Les structures de données doivent être utilisées autant que possible pour organiser et isoler l'utilisation de l'espace de noms global.
La portée variable offre aux programmeurs une protection très utile - mais elle peut avoir un coût. Je suis arrivé à écrire sur les variables globales ce soir parce que je suis un programmeur expérimenté en Objective-C qui est souvent frustré par les barrières que l’objet d’objets orientées sur l’accès aux données. Je dirais que le fanatisme anti-global provient principalement de programmeurs plus jeunes, férus de théorie, expérimentés principalement avec des API orientées objet isolément, sans une expérience pratique approfondie des API système et de leur interaction dans le développement d'applications. Mais je dois admettre que je suis frustré lorsque les fournisseurs utilisent l'espace de noms de manière négligente. Plusieurs distributions Linux avaient par exemple "PI" et "TWOPI" globalement prédéfinies, ce qui cassait beaucoup de mon code personnel.
Vous devez également déterminer dans quel contexte la variable globale sera utilisée. A l'avenir, voulez-vous que ce code soit dupliqué?.
Par exemple, si vous utilisez un socket au sein du système pour accéder à une ressource. À l'avenir, voudrez-vous accéder à plus d'une de ces ressources, si la réponse est oui, je resterais à l'écart des globals afin qu'un refactor majeur ne soit pas nécessaire.
C’est un outil comme un autre, habituellement surutilisé, mais je ne pense pas qu’ils soient diaboliques.
Par exemple, j'ai un programme qui agit vraiment comme une base de données en ligne. Les données sont stockées dans la mémoire mais d’autres programmes peuvent les manipuler. Il existe des routines internes qui ressemblent beaucoup aux procédures stockées et aux déclencheurs d'une base de données.
Ce programme a des centaines de variables globales, mais si vous y réfléchissez, il s’agit d’une base de données mais d’un très grand nombre de variables globales.
Ce programme est utilisé depuis une dizaine d’années depuis plusieurs versions et n’a jamais posé de problème. Je le referais dans une minute.
J'admets que dans ce cas, les vars globaux sont des objets qui ont des méthodes utilisées pour changer l'état de l'objet. Donc, retrouver qui a changé l'objet pendant le débogage n'est pas un problème, car je peux toujours définir un point d'arrêt sur la routine qui change l'état de l'objet. Ou encore plus simple, je viens d'activer la journalisation intégrée qui enregistre les modifications.
Les variables globales doivent être utilisées lorsque plusieurs fonctions doivent accéder aux données ou écrire dans un objet. Par exemple, si vous deviez transmettre des données ou une référence à plusieurs fonctions, telles qu'un seul fichier journal, un pool de connexions ou une référence matérielle à laquelle vous devez accéder via l'application. Cela évite les déclarations de fonction très longues et les allocations importantes de données dupliquées.
Vous devez généralement non utiliser des variables globales, sauf en cas de nécessité absolue, car elles ne sont nettoyées que lorsque le programme vous le demande explicitement ou que votre programme se termine. Si vous exécutez une application multithread, plusieurs fonctions peuvent écrire dans la variable en même temps. Si vous avez un bogue, il peut être plus difficile de le localiser, car vous ne savez pas quelle fonction modifie la variable. Vous rencontrez également le problème des conflits de noms sauf si vous utilisez une convention de noms qui attribue explicitement un nom unique aux variables globales.
Lorsque vous déclarez des constantes.
Je peux penser à plusieurs raisons:
débogage/tests (avertissement - ce code n'a pas été testé):
#include <stdio.h>
#define MAX_INPUT 46
int runs=0;
int fib1(int n){
++runs;
return n>2?fib1(n-1)+fib1(n-2):1;
};
int fib2(int n,int *cache,int *len){
++runs;
if(n<=2){
if(*len==2)
return 1;
*len=2;
return cache[0]=cache[1]=1;
}else if(*len>=n)
return cache[n-1];
else{
if(*len!=n-1)
fib2(n-1,cache,len);
*len=n;
return cache[n-1]=cache[n-2]+cache[n-3];
};
};
int main(){
int n;
int cache[MAX_INPUT];
int len=0;
scanf("%i",&n);
if(!n||n>MAX_INPUT)
return 0;
printf("fib1(%i)==%i",n,fib1(n));
printf(", %i run(s)\n",runs);
runs=0;
printf("fib2(%i)==%i",n,fib2(n,&cache,&len));
printf(", %i run(s)\n",runs);
main();
};
J'ai utilisé des variables étendues pour fib2, mais c'est un scénario de plus où les globales pourraient être utiles (fonctions mathématiques pures qui nécessitent de stocker des données pour éviter de prendre pour toujours).
programmes utilisés une seule fois (par exemple pour un concours) ou lorsque le temps de développement doit être raccourci
les globales sont utiles en tant que constantes typées, où une fonction nécessite quelque part * int au lieu de int.
En général, j'évite les globals si j'ai l'intention d'utiliser le programme plus d'une journée.
Je suis dans le camp "jamais" ici; si vous avez besoin d'une variable globale, utilisez au moins un modèle singleton pattern . De cette façon, vous bénéficiez des avantages d'une instanciation paresseuse et vous n'encombrez pas l'espace de noms global.
Les constantes globales sont utiles - vous obtenez plus de sécurité de type que les macros de préprocesseur et il est tout aussi facile de modifier la valeur si vous le souhaitez.
Les variables globales ont certaines utilisations, par exemple si le fonctionnement de nombreuses parties d'un programme dépend d'un état particulier de la machine à états. Tant que vous limitez le nombre d'emplacements pouvant modifier la variable, il n'est pas si mal de traquer les bogues qui la concernent.
Les variables globales deviennent dangereuses presque dès que vous créez plusieurs threads. Dans ce cas, vous devriez vraiment limiter la portée à (tout au plus) une variable de fichier globale (en la déclarant statique) et des méthodes getter/setter qui le protègent des accès multiples là où cela pourrait être dangereux.