Ich bin relativ neu in der Programmierung und viele der Codierungsempfehlungen, die ich effektiv lese, besagen, dass es nur sehr wenige gute Gründe gibt, eine globale Variable zu verwenden (oder dass der beste Code überhaupt keine globalen Elemente enthält).
Ich habe mein Bestes getan, um dies zu berücksichtigen, wenn ich Software schreibe, um eine Arduino-Schnittstelle mit einer SD-Karte zu erstellen, mit einem Computer zu sprechen und einen Motorcontroller zu betreiben.
Ich habe derzeit 46 Globals für ungefähr 1100 Zeilen "Anfänger" -Code (keine Zeile hat mehr als eine Aktion). Ist das ein gutes Verhältnis oder sollte ich es mehr reduzieren? Welche Methoden kann ich auch anwenden, um die Anzahl der Globalen weiter zu reduzieren?
Ich frage dies hier, weil ich mich speziell mit Best Practices für das Codieren von Arduino-Produkten befasse, und nicht mit Computerprogrammierung im Allgemeinen.
quelle
Antworten:
Sie sind an sich nicht böse , aber sie neigen dazu, überbeansprucht zu werden, wenn es keinen guten Grund gibt, sie zu benutzen.
Es gibt viele Male, in denen globale Variablen von Vorteil sind. Zumal sich die Programmierung eines Arduino unter der Haube stark von der Programmierung eines PCs unterscheidet.
Der größte Vorteil globaler Variablen ist die statische Zuordnung. Besonders bei großen und komplexen Variablen wie Klasseninstanzen. Die dynamische Zuweisung (die Verwendung von
new
usw.) wird aufgrund fehlender Ressourcen verpönt.Außerdem erhalten Sie nicht wie in einem normalen C-Programm einen einzelnen Aufrufbaum (einzelne
main()
Funktion, die andere Funktionen aufruft), sondern zwei separate Bäume (setup()
Funktionen aufrufen, dannloop()
Funktionen aufrufen), was bedeutet, dass manchmal globale Variablen die Variablen sind Nur so können Sie Ihr Ziel erreichen (dh wenn Sie es in beidensetup()
und verwenden möchtenloop()
).Also nein, sie sind nicht böse und auf einem Arduino haben sie mehr und bessere Verwendungsmöglichkeiten als auf einem PC.
quelle
loop()
(oder in mehreren aufgerufenen Funktionenloop()
) benutze ? Wäre es besser, sie anders einzurichten als zu Beginn zu definieren?static
ob ich sie brauche, um ihren Wert über Iterationen hinweg beizubehalten) und sie durch die Funktionsparameter in der gesamten Aufrufkette weiterleiten.void foo(int &var) { var = 4; }
undfoo(n);
-n
ist jetzt 4.Es ist sehr schwierig, eine endgültige Antwort zu geben, ohne Ihren tatsächlichen Code zu sehen.
Globale Variablen sind nicht böse und in einer eingebetteten Umgebung, in der Sie normalerweise häufig auf Hardware zugreifen, oft sinnvoll. Sie haben nur vier UARTS, nur einen I2C-Port usw. Daher ist es sinnvoll, Globals für Variablen zu verwenden, die an bestimmte Hardwareressourcen gebunden sind. Und in der Tat, die Arduino - Core - Bibliothek tut das:
Serial
,Serial1
usw. sind globale Variablen. Außerdem ist eine Variable, die den globalen Status des Programms darstellt, in der Regel global.Geht nicht um die Zahlen. Die richtige Frage, die Sie sich stellen sollten, ist für jeden dieser Globalen, ob es Sinn macht, ihn im globalen Rahmen zu haben.
Trotzdem scheint mir 46 global ein bisschen hoch zu sein. Wenn einige dieser Werte konstant bleiben, qualifizieren Sie sie als
const
: Der Compiler optimiert normalerweise ihren Speicherplatz. Wenn eine dieser Variablen nur in einer einzelnen Funktion verwendet wird, machen Sie sie lokal. Wenn der Wert zwischen den Aufrufen der Funktion bestehen bleiben soll, kennzeichnen Sie diese alsstatic
. Sie können auch die Anzahl der "sichtbaren" Globals reduzieren, indem Sie Variablen innerhalb einer Klasse gruppieren und eine globale Instanz dieser Klasse haben. Aber nur dann, wenn es Sinn macht, Dinge zusammenzustellen. Wenn Sie eine großeGlobalStuff
Klasse erstellen, um nur eine globale Variable zu haben, wird Ihr Code nicht klarer.quelle
static
ob ich eine Variable habe, die nur in einer Funktion verwendet wird, aber jedes Mal, wenn eine Funktion aufgerufen wird (wievar=millis()
) , einen neuen Wert erhalte, sollte ich das machenstatic
?static
Ist nur für den Fall, dass Sie den Wert beibehalten müssen. Wenn Sie als Erstes den Wert der Variablen auf die aktuelle Zeit setzen, müssen Sie den alten Wert nicht beibehalten. In dieser Situation verschwendet die statische Aufladung nur Speicher.Das Hauptproblem bei globalen Variablen ist die Codeverwaltung. Beim Lesen einer Codezeile ist es einfach, die Deklaration von Variablen zu finden, die als Parameter übergeben oder lokal deklariert wurden. Es ist nicht so einfach, die Deklaration globaler Variablen zu finden (oft ist eine IDE erforderlich).
Wenn Sie viele globale Variablen haben (40 ist bereits eine Menge), wird es schwierig, einen expliziten Namen zu haben, der nicht zu lang ist. Mit dem Namespace können Sie die Rolle globaler Variablen verdeutlichen.
Ein schlechter Weg, Namespaces in C nachzuahmen, ist:
Auf Intel- oder Arm-Prozessoren ist der Zugriff auf globale Variablen langsamer als auf andere Variablen. Auf Arduino ist es wahrscheinlich das Gegenteil.
quelle
Obwohl ich sie beim Programmieren für einen PC nicht verwenden würde, haben sie für das Arduino einige Vorteile. Am meisten, wenn es schon gesagt wurde:
In einigen Fällen, insbesondere in Bezug auf die Leistung, kann es auch sinnvoll sein, globale Variablen zu verwenden, anstatt bei Bedarf Elemente zu erstellen:
quelle
Wie bei allem (außer Gotos, die wirklich böse sind) haben Globale ihren Platz.
Beispiel: Wenn Sie eine serielle Debug-Schnittstelle oder eine Protokolldatei haben, in die Sie von überall aus schreiben können müssen, ist es oft sinnvoll, sie global zu gestalten. Wenn Sie wichtige Systemstatusinformationen haben, ist es häufig die einfachste Lösung, diese global zu machen. Es hat keinen Sinn, einen Wert zu haben, den Sie an jede einzelne Funktion im Programm übergeben müssen.
Wie andere gesagt haben, scheint 46 für nur etwas mehr als 1000 Codezeilen eine Menge zu sein, aber ohne die Details zu wissen, was Sie tun, ist es schwer zu sagen, ob Sie sie überflüssig machen oder nicht.
Stellen Sie sich jedoch für jeden Globus ein paar wichtige Fragen:
Ist der Name klar und eindeutig, damit ich nicht versehentlich versuche, den gleichen Namen an einer anderen Stelle zu verwenden? Wenn nicht, ändern Sie den Namen.
Muss sich das jemals ändern? Wenn nicht, machen Sie es zu einer Konstante.
Muss dies überall sichtbar sein oder ist es nur global, damit der Wert zwischen den Funktionsaufrufen erhalten bleibt? Erwägen Sie daher, es für die Funktion lokal zu machen und das Schlüsselwort static zu verwenden.
Wird es wirklich schlimm werden, wenn sich das durch einen Code ändert, wenn ich nicht aufpasse? Wenn Sie z. B. zwei zusammengehörige Variablen haben, z. B. Name und ID-Nummer, die global sind (siehe vorherigen Hinweis zur Verwendung von global, wenn Sie Informationen fast überall benötigen), kann eine davon geändert werden, ohne dass die anderen bösen Dinge passieren. Ja, Sie könnten vorsichtig sein, aber manchmal ist es gut, ein wenig Vorsicht walten zu lassen. Legen Sie sie z. B. in eine andere .c-Datei und definieren Sie dann Funktionen, mit denen Sie beide gleichzeitig festlegen und sie lesen können. Sie fügen dann nur die Funktionen in die zugehörige Header-Datei ein. Auf diese Weise kann der Rest Ihres Codes nur über die definierten Funktionen auf die Variablen zugreifen und so nichts durcheinander bringen.
- update - Ich habe gerade festgestellt, dass Sie eher nach Arduino-spezifischen Best Practices als nach allgemeiner Codierung gefragt haben, und dies ist eher eine allgemeine Codierungsantwort. Aber ehrlich gesagt gibt es keinen großen Unterschied, gute Praxis ist gute Praxis. Die
startup()
undloop()
Struktur von Arduino bedeutet, dass Sie in einigen Situationen ein wenig mehr Globals verwenden müssen als andere Plattformen, aber das ändert nicht wirklich viel. Am Ende streben Sie immer das Beste an, was Sie innerhalb der Grenzen der Plattform tun können, egal was passiert die plattform ist.quelle
goto
s, die darin besteht, aus verschachtelten Schleifen auszubrechen. Sie ist viel sauberer und verständlicher als die Alternativen.goto
sind, dass es böse ist, lesen Sie den Linux-Code. Sie sind wohl genauso böse wietry...catch
Blöcke, wenn sie als solche verwendet werden.Sind sie böse Vielleicht. Das Problem bei Globals ist, dass sie zu jedem Zeitpunkt von einer Funktion oder einem Codeteil, der ausgeführt wird, ohne Einschränkungen aufgerufen und geändert werden können. Dies kann zu Situationen führen, die sich beispielsweise nur schwer zurückverfolgen und erklären lassen. Es ist daher wünschenswert, die Anzahl der Globalen zu minimieren, wenn möglich, um die Anzahl auf Null zurückzusetzen.
Können sie vermieden werden? Fast immer ja Das Problem mit Arduino ist, dass sie Sie zu diesem Ansatz mit zwei Funktionen zwingen, bei dem sie Sie
setup()
und Sie übernehmenloop()
. In diesem speziellen Fall haben Sie (wahrscheinlichmain()
) keinen Zugriff auf den Umfang der Anruferfunktion dieser beiden Funktionen . Wenn Sie dies getan hätten, könnten Sie sich von allen Globalen befreien und stattdessen Einheimische verwenden.Stellen Sie sich Folgendes vor:
Dies ist wahrscheinlich mehr oder weniger die Hauptfunktion eines Arduino-Programms. Variablen, die Sie sowohl in der
setup()
als auch in derloop()
Funktion benötigen, werden dann vorzugsweise im Geltungsbereich der deklariertmain()
Funktionsbereichs und nicht des globalen Bereichs . Sie könnten dann den beiden anderen Funktionen zugänglich gemacht werden, indem sie als Argumente übergeben werden (ggf. unter Verwendung von Zeigern).Beispielsweise:
Beachten Sie, dass Sie in diesem Fall auch die Signatur beider Funktionen ändern müssen.
Da dies möglicherweise weder machbar noch wünschenswert ist, sehe ich wirklich nur einen Weg, um die meisten globalen Elemente aus einem Arduino-Programm zu entfernen, ohne die erzwungene Programmstruktur zu ändern.
Wenn ich mich richtig erinnere, können Sie C ++ perfekt verwenden, während Sie für Arduino programmieren, anstatt C. Wenn Sie (noch) nicht mit OOP (Object Oriented Programming) vertraut sind oder C ++ , ist es möglicherweise gewöhnungsbedürftig lesen.
Mein Vorschlag wäre, eine Program-Klasse und eine einzelne globale Instanz dieser Klasse zu erstellen. Eine Klasse sollte als Blaupause für Objekte betrachtet werden.
Betrachten Sie das folgende Beispielprogramm:
Voilà, wir haben uns von fast allen Globalen befreit. Die Funktionen, in denen Sie beginnen würden, Ihre Anwendungslogik hinzuzufügen, wären die Funktionen
Program::setup()
undProgram::loop()
. Diese Funktionen haben Zugriff auf die Instanz bestimmten ElementvariablenmyFirstSampleVariable
undmySecondSampleVariable
während der traditionellensetup()
undloop()
Funktionen haben keinen Zugriff , da diese Variablen Klasse privat markiert hat. Dieses Konzept wird als Datenkapselung oder Verstecken von Daten bezeichnet.Das Unterrichten von OOP und / oder C ++ ist in der Beantwortung dieser Frage nicht ausreichend, daher höre ich hier auf.
Zusammenfassend lässt sich sagen, dass Globals vermieden werden sollten und es fast immer möglich ist, die Anzahl der Globals drastisch zu reduzieren. Auch wenn Sie für Arduino programmieren.
Vor allem hoffe ich, dass meine Antwort für Sie nützlich ist :)
quelle
Program::instance().setup()
Stattdessen greifen Sie einfach über zuglobalProgram.setup()
. Das Einfügen verwandter globaler Variablen in eine Klasse / Struktur / einen Namespace kann von Vorteil sein, insbesondere wenn sie nur von einigen verwandten Funktionen benötigt werden, das Singleton-Muster jedoch nichts hinzufügt. Mit anderen Worten,static Program p;
verfügt über globalen Speicher undstatic Program& instance()
hat globalen Zugriff, was genauso viel wie einfach istProgram globalProgram;
.Globale Variablen sind niemals böse . Eine allgemeine Regel gegen sie ist nur eine Krücke, mit der Sie lange genug überleben können, um Erfahrungen zu sammeln und bessere Entscheidungen zu treffen.
Was eine globale Variable ist, ist eine inhärente Annahme, dass es nur eine einzige Sache gibt (es spielt keine Rolle, ob es sich um ein globales Array oder eine Map handelt, die möglicherweise mehrere Dinge enthält, die aber immer noch die Annahme enthalten, dass es nur eine gibt) eine solche Liste oder Zuordnung und nicht mehrere unabhängige).
Bevor Sie ein globales System nutzen, möchten Sie sich fragen: Ist es denkbar, dass ich jemals mehr als eines davon verwenden möchte? Wenn es sich später als wahr herausstellt, müssen Sie den Code ändern, um die Globalisierung des Objekts aufzuheben, und Sie werden wahrscheinlich feststellen, dass andere Teile Ihres Codes von dieser Annahme der Eindeutigkeit abhängen Ich muss sie auch reparieren, und der Prozess wird mühsam und fehleranfällig. "Verwenden Sie keine Globals" wird gelehrt, weil es normalerweise recht wenig kostet, Globals von Anfang an zu vermeiden, und es vermeidet das Potenzial, später große Kosten bezahlen zu müssen.
Aber die vereinfachenden Annahmen, die Globals erlauben, machen Ihren Code auch kleiner, schneller und verbrauchen weniger Speicher, weil er nicht um eine Vorstellung herumgeben muss, welche Sache er verwendet, nicht umleiten muss, nicht umleiten muss Berücksichtigen Sie die Möglichkeit, dass das gewünschte Objekt möglicherweise nicht vorhanden ist usw. Bei eingebetteten Systemen ist die Wahrscheinlichkeit höher, dass die Codegröße und / oder die CPU-Zeit und / oder der Arbeitsspeicher eingeschränkt sind, als bei einem PC. Daher können diese Einsparungen von Bedeutung sein. Und viele eingebettete Anwendungen haben auch höhere Anforderungen - Sie wissen, dass Ihr Chip nur ein bestimmtes Peripheriegerät hat, der Benutzer kann nicht einfach ein anderes an einen USB-Anschluss anschließen oder so.
Ein weiterer häufiger Grund für den Wunsch nach mehr als einer Funktion, die eindeutig zu sein scheint, ist das Testen - das Testen der Interaktion zwischen zwei Komponenten ist einfacher, wenn Sie lediglich eine Testinstanz einer Komponente an eine Funktion übergeben können, während das Ändern des Verhaltens einer globalen Komponente der Grund ist eine kniffligere Angelegenheit. Das Testen in der Embedded-Welt unterscheidet sich jedoch in der Regel erheblich von anderen Anbietern. Daher trifft dies möglicherweise nicht auf Sie zu. Soweit ich weiß, hat Arduino überhaupt keine Testkultur.
Setzen Sie also Globals ein, wenn es sich zu lohnen scheint. Die Codepolizei wird Sie nicht abholen. Wisse nur, dass die falsche Wahl später zu viel mehr Arbeit für dich führen könnte. Wenn du dir also nicht sicher bist ...
quelle
Nichts ist von Natur aus böse, einschließlich globaler Variablen. Ich würde es als "notwendiges Übel" bezeichnen - es kann Ihr Leben viel einfacher machen, aber es sollte mit Vorsicht angegangen werden.
Verwenden Sie Wrapper-Funktionen, um auf Ihre globalen Variablen zuzugreifen. Sie verwalten es also zumindest aus der Scope-Perspektive.
quelle