Ich habe angefangen, Firmware für mein Produkt zu schreiben, und ich bin hier ein Neuling. Ich habe viele Artikel darüber gelesen, wie man keine globalen Variablen oder Funktionen verwendet. Gibt es eine Begrenzung für die Verwendung globaler Variablen in einem 8-Bit-System oder ist es ein komplettes "Nein-Nein". Wie sollte ich globale Variablen in meinem System verwenden oder sollte ich sie vollständig vermeiden?
Ich würde gerne wertvolle Ratschläge von euch zu diesem Thema annehmen, um meine Firmware kompakter zu machen.
Sie können globale Variablen erfolgreich verwenden, solange Sie die Richtlinien von @Phil beachten. Hier sind jedoch einige nette Möglichkeiten, ihre Probleme zu vermeiden, ohne den kompilierten Code weniger kompakt zu machen.
Verwenden Sie lokale statische Variablen für den dauerhaften Zustand, auf den Sie nur innerhalb einer Funktion zugreifen möchten.
#include <stdint.h>
void skipper()
{
static uint8_t skip_initial_cycles = 5;
if (skip_initial_cycles > 0) {
skip_initial_cycles -= 1;
return;
}
/* ... */
}
Verwenden Sie eine Struktur, um verwandte Variablen zusammenzuhalten, um klarer zu machen, wo sie verwendet werden sollten und wo nicht.
struct machine_state {
uint8_t level;
uint8_t error_code;
} machine_state;
struct led_state {
uint8_t red;
uint8_t green;
uint8_t blue;
} led_state;
void machine_change_state()
{
machine_state.level += 1;
/* ... */
/* We can easily remember not to use led_state in this function. */
}
void machine_set_io()
{
switch (machine_state.level) {
case 1:
PIN_MACHINE_IO_A = 1;
/* ... */
}
}
Verwenden Sie globale statische Variablen, um die Variablen nur innerhalb der aktuellen C-Datei sichtbar zu machen. Dadurch wird ein versehentlicher Zugriff durch Code in anderen Dateien aufgrund von Namenskonflikten verhindert.
/* time_machine.c */
static uint8_t current_time;
/* ... */
/* delay.c */
static uint8_t current_time; /* A completely separate variable for this C file only. */
/* ... */
Als letzte Anmerkung, wenn Sie eine globale Variable innerhalb einer Interrupt-Routine ändern und an anderer Stelle lesen:
volatile
.ODER
static
verhindert, dass der Compiler sie verwirrt, wenn Sie diesen Fehler machen.volatile
Variablen besteht darin, Code, der in einem Ausführungskontext ausgeführt wird, zu ermöglichen, Code in einem anderen Ausführungskontext wissen zu lassen, dass etwas passiert ist. Auf einem 8-Bit-System kann ein Puffer, der eine Zweierpotenz von Bytes von nicht mehr als 128 halten wird, mit einem flüchtigen Byte verwaltet werden, das die Gesamtlebensdauer der Bytes angibt, die in den Puffer gelegt werden (mod 256) und ein anderer zeigt die Lebenszeitzahl der herausgenommenen Bytes an, vorausgesetzt, dass nur ein Ausführungskontext Daten in den Puffer legt und nur einer Daten daraus entnimmt.Die Gründe, warum Sie in einem 8-Bit-System keine globalen Variablen verwenden möchten, sind die gleichen, aus denen Sie sie in keinem anderen System verwenden möchten: Sie erschweren das Nachdenken über das Verhalten des Programms.
Nur schlechte Programmierer hängen sich an Regeln wie "keine globalen Variablen verwenden" auf. Gute Programmierer verstehen den Grund hinter den Regeln und behandeln die Regeln dann eher wie Richtlinien.
Ist Ihr Programm leicht verständlich? Ist sein Verhalten vorhersehbar? Ist es einfach, Teile davon zu modifizieren, ohne andere Teile zu beschädigen? Wenn die Antwort auf jede dieser Fragen Ja ist , dann sind Sie auf dem Weg zu einem guten Programm.
Auf die Verwendung globaler Variablen (kurz "Globals") sollten Sie nicht ganz verzichten. Aber Sie sollten sie mit Bedacht einsetzen. Die praktischen Probleme bei exzessivem Einsatz von Globals:
g_
Es empfiehlt sich, dem Namen globaler Variablen ein Präfix hinzuzufügen . Zum Beispiel g_iFlags
. Wenn Sie die Variable mit dem Präfix im Code sehen, erkennen Sie sofort, dass es sich um eine globale Variable handelt.
static
Flagge für die sichtbar werden main()
? Bedeuten Sie, dass dieselbe Funktion, die das hat static
, es main()
später zurückgeben kann?Der Vorteil globaler Datenstrukturen in eingebetteten Arbeiten besteht darin, dass sie statisch sind. Wenn jede Variable, die Sie brauchen, global ist, dann wird Ihnen nie versehentlich der Speicher ausgehen, wenn Funktionen eingegeben werden und Platz für sie auf dem Stack geschaffen wird. Aber warum haben sie dann an diesem Punkt Funktionen? Warum nicht eine große Funktion, die die gesamte Logik und alle Prozesse handhabt - wie ein BASIC-Programm ohne erlaubtes GOSUB. Wenn Sie diese Idee weit genug treiben, haben Sie ein typisches Assemblerprogramm aus den 1970er Jahren. Effizient und unmöglich zu warten und Fehler zu beheben.
Verwenden Sie also Globals mit Bedacht, wie Zustandsvariablen (z. B. wenn jede Funktion wissen muss, ob sich das System im Interpretations- oder Ausführungszustand befindet) und andere Datenstrukturen, die von vielen Funktionen gesehen werden müssen und wie @PhilFrost sagt, das Verhalten von ist Ihre Funktionen vorhersehbar? Gibt es eine Möglichkeit, den Stapel mit einem Eingabestring zu füllen, der niemals endet? Dies sind Angelegenheiten für den Entwurf von Algorithmen.
Beachten Sie, dass Statik innerhalb und außerhalb einer Funktion unterschiedliche Bedeutung hat. https://stackoverflow.com/questions/5868947/difference-between-static-variable-inside-and-outside-of-a-function
https://stackoverflow.com/questions/5033627/static-variable-inside-of-a-function-in-c
Globale Variablen sollten nur für wirklich globale Zustände verwendet werden. Die Verwendung einer globalen Variablen, um so etwas wie zB den Breitengrad der nördlichen Kartengrenze darzustellen, funktioniert nur, wenn es immer nur eine "nördliche Kartengrenze" geben kann. Wenn der Code in Zukunft mit mehreren Karten arbeiten muss, die unterschiedliche nördliche Grenzen haben, muss Code, der eine globale Variable für die nördliche Grenze verwendet, wahrscheinlich überarbeitet werden.
Bei typischen Computeranwendungen gibt es oft keinen besonderen Grund anzunehmen, dass es nie mehr als eine von etwas geben wird. In eingebetteten Systemen sind solche Annahmen jedoch oft viel vernünftiger. Während es möglich ist, dass ein typisches Computerprogramm aufgerufen wird, um mehrere gleichzeitige Benutzer zu unterstützen, ist die Benutzerschnittstelle eines typischen eingebetteten Systems für die Bedienung durch einen einzelnen Benutzer ausgelegt, der mit seinen Tasten und seiner Anzeige interagiert. Als solches wird es zu jedem Zeitpunkt einen einzigen Benutzerschnittstellenzustand haben. Das System so zu entwerfen, dass mehrere Benutzer mit mehreren Tastaturen und Displays interagieren könnten, würde viel mehr Komplexität erfordern und viel länger in der Implementierung dauern, als es für einen einzelnen Benutzer zu entwerfen. Wenn das System nie aufgefordert wird, mehrere Benutzer zu unterstützen, jeder zusätzliche Aufwand, der investiert wird, um eine solche Verwendung zu erleichtern, wird verschwendet. Sofern es nicht wahrscheinlich ist, dass Mehrbenutzerunterstützung erforderlich ist, wäre es wahrscheinlich klüger, das Risiko einzugehen, den für eine Einzelbenutzeroberfläche verwendeten Code verwerfen zu müssen, falls Mehrbenutzerunterstützung erforderlich ist, als zusätzliche Zeit damit zu verbringen, Mehrbenutzer hinzuzufügen. Benutzerunterstützung, die wahrscheinlich nie benötigt wird.
Ein verwandter Faktor bei eingebetteten Systemen ist, dass in vielen Fällen (insbesondere bei Benutzerschnittstellen) die einzige praktische Möglichkeit, mehr als einen von etwas zu unterstützen, darin besteht, mehrere Threads zu verwenden. In Ermangelung einer anderen Notwendigkeit für Multi-Threading ist es wahrscheinlich besser, ein einfaches Single-Threading-Design zu verwenden, als die Systemkomplexität mit Multi-Threading zu erhöhen, das wahrscheinlich nie wirklich notwendig ist. Wenn das Hinzufügen von mehr als einem von etwas sowieso eine riesige Neugestaltung des Systems erfordern würde, spielt es keine Rolle, ob es auch eine Überarbeitung der Verwendung einiger globaler Variablen erfordert.
Viele Menschen sind verwirrt über dieses Thema. Die Definition einer globalen Variablen lautet:
Etwas, das von überall in Ihrem Programm zugänglich ist.
Dies ist nicht dasselbe wie Dateibereichsvariablen , die durch das Schlüsselwort deklariert werden static
. Das sind keine globalen Variablen, sondern lokale private Variablen.
int x; // global variable
static int y; // file scope variable
void some_func (void) {...} // added to demonstrate that the variables above are at file scope.
Sollten Sie globale Variablen verwenden? Es gibt ein paar Fälle, in denen es in Ordnung ist:
In allen anderen Fällen dürfen Sie niemals globale Variablen verwenden. Es gibt nie einen Grund dafür. Verwenden Sie stattdessen Dateibereichsvariablen , was vollkommen in Ordnung ist.
Sie sollten sich bemühen, unabhängige, autonome Codemodule zu schreiben, die für eine bestimmte Aufgabe entwickelt wurden. Innerhalb dieser Module sollten sich interne Dateibereichsvariablen als private Datenmitglieder befinden. Diese Entwurfsmethode ist als Objektorientierung bekannt und weithin als gutes Design anerkannt.
.data
Segment ist.
Lundin
Endolith
Lundin
static
file scope ist nicht dasselbe wie "global", siehe meine Antwort unten.