Je suis relativement nouveau en programmation et bon nombre des meilleures pratiques de codage que je lis indiquent effectivement qu'il y a très peu de bonnes raisons d'utiliser une variable globale (ou que le meilleur code n'a pas de globaux du tout).
J'ai fait de mon mieux pour garder cela à l'esprit, lors de l'écriture d'un logiciel pour créer une interface Arduino avec une carte SD, parler à un ordinateur et exécuter un contrôleur de moteur.
J'ai actuellement 46 globales pour environ 1100 lignes de code "niveau débutant" (aucune ligne ayant plus d'une action). Est-ce un bon ratio ou devrais-je envisager de le réduire davantage? Quelles pratiques puis-je utiliser pour réduire davantage le nombre de personnes dans le monde?
Je pose cette question ici parce que je suis spécifiquement préoccupé par les meilleures pratiques de codage sur les produits Arduino plutôt que par la programmation informatique en général.
la source
Réponses:
Ils ne sont pas mauvais en soi, mais ils ont tendance à être surutilisés là où il n'y a aucune bonne raison de les utiliser.
Il y a de nombreuses fois où les variables globales sont avantageuses. D'autant plus que la programmation d'un Arduino est, sous le capot, très différente de la programmation d'un PC.
Le plus grand avantage des variables globales est l'allocation statique. Surtout avec des variables grandes et complexes telles que les instances de classe. L'allocation dynamique (l'utilisation de
new
etc.) est mal vue en raison du manque de ressources.De plus, vous n'obtenez pas une seule arborescence d'appels comme vous le faites dans un programme C normal (une seule
main()
fonction appelant d'autres fonctions) - au lieu de cela, vous obtenez effectivement deux arborescences distinctes (setup()
appelant des fonctions, puisloop()
appelant des fonctions), ce qui signifie que parfois les variables globales sont les seule façon d'atteindre votre objectif ( par exemple, si vous voulez l' utiliser à la foissetup()
etloop()
).Donc non, ils ne sont pas mauvais, et sur un Arduino, ils ont plus et de meilleures utilisations que sur un PC.
la source
loop()
(ou dans plusieurs fonctions appeléesloop()
)? serait-il préférable de les mettre en place différemment que de les définir au départ?static
si j'avais besoin d'eux pour conserver leur valeur à travers les itérations) et les passerais par les paramètres de fonction le long de la chaîne d'appel.void foo(int &var) { var = 4; }
etfoo(n);
- an
maintenant 4 ans.Il est très difficile de donner une réponse définitive sans voir votre code réel.
Les variables globales ne sont pas mauvaises, et elles ont souvent un sens dans un environnement intégré où vous faites généralement beaucoup d'accès matériel. Vous n'avez que quatre UARTS, un seul port I2C, etc. Il est donc logique d'utiliser des variables globales pour des variables liées à des ressources matérielles spécifiques. Et en effet, la bibliothèque de base Arduino fait que:
Serial
,Serial1
, etc. sont des variables globales. En outre, une variable représentant l'état global du programme est généralement un global.Ne concerne pas les chiffres. La bonne question que vous devriez vous poser est, pour chacun de ces globaux, s'il est logique de l'avoir à l'échelle mondiale.
Pourtant, 46 global me semble un peu élevé. Si certains d'entre eux contiennent des valeurs constantes, qualifiez-les comme
const
: le compilateur optimise généralement leur stockage. Si l'une de ces variables n'est utilisée que dans une seule fonction, rendez-la locale. Si vous souhaitez que sa valeur persiste entre les appels à la fonction, qualifiez-la commestatic
. Vous pouvez également réduire le nombre de globales «visibles» en regroupant les variables dans une classe et en ayant une instance globale de cette classe. Mais ne le faites que lorsque cela a du sens de rassembler des choses. Faire une grandeGlobalStuff
classe pour avoir une seule variable globale n'aidera pas à rendre votre code plus clair.la source
static
si j'avais une variable qui n'est utilisée que dans une fonction, mais j'obtiens une nouvelle valeur chaque fois qu'une fonction est appelée (commevar=millis()
) dois-je faire celastatic
?static
c'est uniquement lorsque vous devez conserver la valeur. Si la première chose que vous faites dans la fonction est de définir la valeur de la variable à l'heure actuelle, il n'est pas nécessaire de conserver l'ancienne valeur. Tout ce que statique fait dans cette situation, c'est perdre de la mémoire.Le principal problème avec les variables globales est la maintenance du code. Lors de la lecture d'une ligne de code, il est facile de trouver la déclaration des variables passées en paramètre ou déclarées localement. Il n'est pas si facile de trouver une déclaration de variables globales (souvent cela nécessite un IDE).
Lorsque vous avez plusieurs variables globales (40 c'est déjà beaucoup), il devient difficile d'avoir un nom explicite qui n'est pas trop long. L'utilisation de l'espace de noms est un moyen de clarifier le rôle des variables globales.
Une mauvaise façon d'imiter les espaces de noms en C est:
Sur un processeur Intel ou Arm, l'accès aux variables globales est plus lent que les autres variables. C'est probablement le contraire sur Arduino.
la source
Bien que je ne les utilise pas lors de la programmation pour un PC, pour l'Arduino, ils présentent certains avantages. Surtout si cela a déjà été dit:
De plus, dans certains cas, en particulier en termes de performances, il peut être bon d'utiliser des variables globales, au lieu de créer des éléments en cas de besoin:
la source
Comme pour tout (à part les gotos qui sont vraiment mauvais), les globaux ont leur place.
Par exemple, si vous avez un port série de débogage ou un fichier journal sur lequel vous devez pouvoir écrire de partout, il est souvent judicieux de le rendre global. De même, si vous avez des informations critiques sur l'état du système, la rendre globale est souvent la solution la plus simple. Il est inutile d'avoir une valeur que vous devez transmettre à chaque fonction du programme.
Comme d'autres l'ont dit, 46 semble beaucoup pour seulement un peu plus de 1000 lignes de code, mais sans connaître les détails de ce que vous faites, il est difficile de dire si vous les utilisez trop ou non.
Cependant, pour chaque global, posez-vous quelques questions importantes:
Le nom est-il clair et précis pour que je n'essaye pas accidentellement d'utiliser le même nom ailleurs? Sinon changez le nom.
Cela doit-il jamais changer? Sinon, envisagez d'en faire un const.
Est-ce que cela doit être visible partout ou est-ce seulement global pour que la valeur soit maintenue entre les appels de fonction? Envisagez donc de le rendre local à la fonction et d'utiliser le mot-clé static.
Est-ce que les choses vont vraiment mal se passer si cela est modifié par un morceau de code lorsque je ne fais pas attention? Par exemple, si vous avez deux variables liées, par exemple le nom et le numéro d'identification, qui sont globales (voir la note précédente sur l'utilisation de global lorsque vous avez besoin d'informations un peu partout), alors si l'une d'entre elles est modifiée sans que les autres choses désagréables puissent se produire. Oui, vous pouvez simplement être prudent, mais il est parfois bon d'appliquer un peu la prudence. par exemple, placez-les dans un fichier .c différent, puis définissez les fonctions qui les définissent en même temps et vous permettent de les lire. Vous n'incluez alors que les fonctions dans le fichier d'en-tête associé, de cette façon, le reste de votre code ne peut accéder qu'aux variables via les fonctions définies et ne peut donc pas gâcher les choses.
- mise à jour - Je viens de réaliser que vous aviez posé des questions sur les meilleures pratiques spécifiques à Arduino plutôt que sur le codage général et il s'agit plus d'une réponse de codage général. Mais honnêtement, il n'y a pas beaucoup de différence, une bonne pratique est une bonne pratique. La
startup()
et laloop()
structure des moyens Arduino que vous devez utiliser GLOBALS un peu plus que d' autres plates - formes dans certaines situations , mais cela ne change pas vraiment beaucoup, vous finissez toujours viser le meilleur que vous pouvez faire dans les limites de la plate - forme , peu importe ce que la plate-forme est.la source
goto
s et c'est de sortir des boucles imbriquées, c'est beaucoup plus propre et plus facile à comprendre que les alternatives.goto
c'est mal, consultez le code Linux. On peut dire qu'ils sont tout aussi mauvais que destry...catch
blocs lorsqu'ils sont utilisés comme tels.Sont-ils mauvais? Peut être. Le problème avec les globaux est qu'ils peuvent être consultés et modifiés à tout moment par n'importe quelle fonction ou morceau de code en cours d'exécution, sans restrictions. Cela peut conduire à des situations qui sont, disons, difficiles à retracer et à expliquer. Il est donc souhaitable de minimiser la quantité de globaux, si possible de ramener la quantité à zéro.
Peut-on les éviter? Presque toujours oui. Le problème avec Arduino est qu'ils vous forcent dans cette approche à deux fonctions dans laquelle ils vous supposent
setup()
et vousloop()
. Dans ce cas particulier, vous n'avez pas accès à l'étendue de la fonction appelant de ces deux fonctions (probablementmain()
). Si vous l'aviez fait, vous seriez en mesure de vous débarrasser de tous les globaux et d'utiliser plutôt des locaux.Imaginez ce qui suit:
C'est probablement plus ou moins à quoi ressemble la fonction principale d'un programme Arduino. Les variables dont vous avez besoin à la fois dans
setup()
laloop()
fonction et dans la fonction seraient alors de préférence déclarées à l'intérieur de la portée de lamain()
fonction plutôt que de la portée globale. Ils pourraient alors être rendus accessibles aux deux autres fonctions en les passant comme arguments (en utilisant des pointeurs si nécessaire).Par exemple:
Notez que dans ce cas, vous devez également modifier la signature des deux fonctions.
Comme cela pourrait ne pas être faisable ni souhaitable, je ne vois vraiment qu'une seule façon de supprimer la plupart des globaux d'un programme Arduino sans modifier la structure du programme forcé.
Si je me souviens bien, vous êtes parfaitement capable d'utiliser C ++ lors de la programmation pour Arduino, plutôt que C. Si vous n'êtes pas (encore) familier avec OOP (Object Oriented Programming) ou C ++, cela pourrait prendre un certain temps pour s'y habituer et d'autres en train de lire.
Ma proposition serait de créer une classe Program et de créer une seule instance globale de cette classe. Une classe doit être considérée comme le modèle des objets.
Considérez l'exemple de programme suivant:
Voilà, nous nous sommes débarrassés de presque tous les mondiaux. Les fonctions dans lesquelles vous commenceriez à ajouter votre logique d'application seraient les fonctions
Program::setup()
etProgram::loop()
. Ces fonctions ont accès aux variables membres spécifiques à l'instancemyFirstSampleVariable
etmySecondSampleVariable
alors que les fonctions traditionnellessetup()
etloop()
n'ont pas accès car ces variables ont été marquées class private. Ce concept est appelé encapsulation ou masquage de données.Vous enseigner la POO et / ou le C ++ est un peu hors de portée de la réponse à cette question, je vais donc m'arrêter ici.
Pour résumer: les globaux doivent être évités et il est presque toujours possible de réduire considérablement le nombre de globaux. Aussi lorsque vous programmez pour Arduino.
Plus important encore, j'espère que ma réponse vous sera quelque peu utile :)
la source
Program::instance().setup()
place deglobalProgram.setup()
. Placer des variables globales liées dans une classe / structure / espace de noms peut être bénéfique, surtout si elles ne sont nécessaires que par quelques fonctions liées, mais le modèle singleton n'ajoute rien. En d'autres termes,static Program p;
dispose d'un stockage global etstatic Program& instance()
d'un accès global, ce qui revient au même que simplementProgram globalProgram;
.Les variables globales ne sont jamais mauvaises . Une règle générale contre eux est juste une béquille pour vous permettre de survivre assez longtemps pour acquérir de l'expérience et prendre de meilleures décisions.
Qu'est-ce qu'une variable globale, c'est une supposition inhérente qu'il n'y a qu'une seule chose (peu importe si nous parlons d'un tableau ou d'une carte globale qui pourrait contenir plusieurs choses, qui contient toujours l'hypothèse qu'il n'y a que une telle liste ou mappage, et non plusieurs indépendants).
Alors avant d'utiliser un global, vous voulez vous demander: est-il concevable que je veuille jamais utiliser plus d'un de ces trucs? S'il s'avère vrai sur toute la ligne, vous devrez modifier le code pour dé-globaliser cette chose, et vous découvrirez probablement en cours de route que d'autres parties de votre code dépendent de cette hypothèse d'unicité, de sorte que vous 'devra également les corriger, et le processus devient fastidieux et sujet aux erreurs. "N'utilisez pas les globaux" est enseigné parce que généralement c'est un coût assez faible pour éviter les globaux dès le début, et cela évite le potentiel d'avoir à payer un coût élevé plus tard.
Mais les hypothèses simplificatrices permises par les globaux rendent également votre code plus petit, plus rapide et utilisent moins de mémoire, car il n'a pas à faire circuler la notion de ce qu'il utilise, n'a pas à faire d'indirection, n'a pas à envisagez la possibilité que la chose désirée n'existe peut-être pas, etc. Sur l'embarqué, vous êtes plus susceptible d'être contraint pour la taille du code et / ou le temps CPU et / ou la mémoire que sur un PC, donc ces économies peuvent avoir de l'importance. Et de nombreuses applications embarquées ont également plus de rigidité dans les exigences - vous savez que votre puce n'a qu'un périphérique particulier, l'utilisateur ne peut pas simplement en brancher une autre sur un port USB ou quelque chose.
Une autre raison courante de vouloir plus d'une chose qui semble unique est le test - tester l'interaction entre deux composants est plus facile lorsque vous pouvez simplement passer une instance de test d'un composant à une fonction, alors que tenter de modifier le comportement d'un composant global est une proposition plus délicate. Mais les tests dans le monde intégré ont tendance à être très différents des autres, donc cela peut ne pas s'appliquer à vous. Pour autant que je sache, Arduino n'a aucune culture de test.
Alors allez-y et utilisez les globaux quand cela semble utile. La police du code ne viendra pas vous chercher. Sachez simplement que le mauvais choix pourrait entraîner beaucoup plus de travail pour vous sur la route, donc si vous n'êtes pas sûr ...
la source
rien n'est intrinsèquement mauvais, y compris les variables globales. Je le qualifierais de "mal nécessaire" - cela peut vous faciliter la vie mais il faut l'aborder avec prudence.
utilisez des fonctions wrapper pour accéder à vos variables globales. vous le gérez donc au moins du point de vue de la portée.
la source