Was ist der Unterschied zwischen der Deklaration einer Variablen außerhalb der Schleife und der Deklaration der statischen Innenschleife?


9

Dies sind zwei Möglichkeiten, wie ich eine Variable außerhalb der Schleife (oder einer beliebigen Funktion) halten kann.

Erstens kann ich es mit globalem Geltungsbereich außerhalb der Schleife deklarieren:

void setup()
{
    Serial.begin(9600);
}

int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    delay(250);
}

Ich kann es auch innerhalb der Schleife als statisch deklarieren:

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    static int count = 0;

    Serial.println(count);
    count++;

    delay(250);
}

Welchen Unterschied wird dies machen, wenn überhaupt?

Antworten:


10

Der grundlegendste Unterschied liegt im Umfang.

Im ersten Fall deklarieren Sie eine globale Variable. Es ist eine Variable, auf die nach ihrer Definition in jedem Bereich zugegriffen werden kann.

void setup()
{
    Serial.begin(9600);
}

void inc();
int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

void inc() //Can edit the value of count
{
  count=count+1;
};

Im zweiten Fall deklarieren Sie eine statische Variable mit lokalem Gültigkeitsbereich. Die Variable bleibt für den gesamten Programmlauf ähnlich wie globale Variablen bestehen, ist jedoch nur in dem Codeblock verfügbar, in dem sie deklariert ist. Dies ist das gleiche Beispiel mit nur einer Änderung. countwird jetzt als statische Variable im Inneren deklariert loop.

void inc();

void loop()
{
    static int count = 0;
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

Dies wird nicht kompiliert, da die Funktion inc()keinen Zugriff hat count.

Globale Variablen, wie nützlich sie auch erscheinen mögen, bergen einige Fallstricke. Diese können sogar Schäden verursachen, wenn Programme geschrieben werden, die mit der physischen Umgebung interagieren können. Dies ist ein sehr einfaches Beispiel für etwas, das sehr wahrscheinlich passieren wird, sobald Programme größer werden. Eine Funktion kann versehentlich den Status einer globalen Variablen ändern.

void setup()
{
    Serial.begin(9600);
}
void another_function();
int state=0;

void loop()
{
    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Inadvertently changes state
  state=1;

}

Solche Fälle sind sehr schwer zu debuggen. Diese Art von Problem kann jedoch leicht erkannt werden, indem einfach eine statische Variable verwendet wird.

void setup()
{
    Serial.begin(9600);
}
void another_function();

void loop()
{
    static int state=0;

    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Results in a compile time error. Saves time.
  state=1;

}

5

Aus funktionaler Sicht generieren beide Versionen das gleiche Ergebnis, da in beiden Fällen der Wert von countzwischen den Ausführungen von gespeichert wird loop()(entweder weil es sich um eine globale Variable handelt oder weil er als markiert ist staticund daher seinen Wert behält).

Die Entscheidung für die Wahl hängt also von folgenden Argumenten ab:

  1. Im Allgemeinen wird in der Informatik empfohlen, Ihre Variablen in Bezug auf den Umfang so lokal wie möglich zu halten . Dies führt normalerweise zu einem viel klareren Code mit weniger Nebenwirkungen und verringert die Wahrscheinlichkeit, dass jemand anderes diese globale Variable verwendet, die Ihre Logik durcheinander bringt. In Ihrem ersten Beispiel können andere Logikbereiche den countWert ändern , während im zweiten Beispiel nur diese bestimmte Funktion dies loop()kann.
  2. Globale und statische Variablen belegen immer den Speicher , während Einheimische dies nur tun, wenn sie sich im Gültigkeitsbereich befinden. In Ihren obigen Beispielen macht das keinen Unterschied (da Sie in einem eine globale, in dem anderen eine statische Variable verwenden), aber in größeren und komplexeren Programmen kann dies dazu führen, dass Sie mit nicht statischen Einheimischen Speicherplatz sparen. Allerdings : Wenn Sie eine Variable in einem Logikbereich haben, die sehr oft ausgeführt wird, sollten Sie sie entweder statisch oder global machen, da Sie sonst bei jeder Eingabe dieses Logikbereichs ein kleines Stück Leistung verlieren, da dies etwas Zeit in Anspruch nimmt Ordnen Sie den Speicher für diese neue Variableninstanz zu. Sie müssen ein Gleichgewicht zwischen Speicherlast und Leistung finden.
  3. Andere Punkte wie ein besseres Layout für die statische Analyse oder die Optimierung durch den Compiler könnten ebenfalls ins Spiel kommen.
  4. In einigen speziellen Szenarien kann es zu Problemen mit der unvorhersehbaren Initialisierungsreihenfolge statischer Elemente kommen (Sie sind sich über diesen Punkt nicht sicher, vergleichen Sie diesen Link jedoch).

Quelle: Ähnlicher Thread auf arduino.cc


Wiedereintritt sollte bei Arduino niemals ein Problem sein, da es keine Parallelität unterstützt.
Peter Bloomfield

Wahr. Das war eher ein allgemeiner Punkt, aber für Arduino in der Tat nicht relevant. Ich habe das bisschen entfernt.
Philip Allgaier

1
Eine statische Variable, die in einem Bereich deklariert ist, ist immer vorhanden und verwendet denselben Speicherplatz wie eine globale Variable! Im OP-Code besteht der einzige Unterschied darin, welcher Code auf die Variable zugreifen kann. In Scipe Static wird im selben Bereich zugänglich sein.
jfpoilpret

1
@jfpoilpret Das ist natürlich wahr und ich sehe, dass der jeweilige Teil in meiner Antwort etwas irreführend war. Das wurde behoben.
Philip Allgaier

2

Beide Variablen sind statisch - sie bleiben für die gesamte Ausführungssitzung bestehen. Das Globale ist für jede Funktion sichtbar, wenn es das Globale deklariert - nicht definiert - oder wenn die Funktion der Definition in derselben Kompilierungseinheit folgt (Datei + enthält).

Wenn Sie die Definition von countin eine Funktion verschieben, wird der Sichtbarkeitsbereich auf den nächsten umschließenden Satz von {}es beschränkt und die Lebensdauer des Funktionsaufrufs erhöht (er wird beim Ein- und Beenden der Funktion erstellt und zerstört). Wenn Sie statices ebenfalls deklarieren, wird die Lebensdauer der Ausführungssitzung vom Anfang bis zum Ende der Ausführungssitzung verlängert und bleibt über Funktionsaufrufe hinweg bestehen.

Übrigens: Seien Sie vorsichtig bei der Verwendung der initialisierten Statik innerhalb einer Funktion, da ich gesehen habe, dass einige Versionen des Gnu-Compilers dies falsch verstehen. Bei jedem Funktionseintrag sollte eine automatische Variable mit einem Initialisierer erstellt und initialisiert werden. Eine Statik mit einem Initialisierer sollte während des Ausführungs-Setups nur einmal initialisiert werden, bevor main () die Kontrolle erhält (genau wie ein globaler). Ich habe lokale Statiken bei jedem Funktionseintrag neu initialisieren lassen, als wären sie Automatiken, was falsch ist. Testen Sie Ihren eigenen Compiler, um sicherzugehen.


Ich bin nicht sicher, ob ich verstehe, was Sie mit einer Funktion meinen, die eine globale deklariert. Meinst du als extern?
Peter Bloomfield

@ PeterR.Bloomfield: Ich bin mir nicht sicher, nach welchem ​​Teil meines Beitrags Sie gefragt haben, aber ich habe mich auf die beiden Beispiele des OP bezogen - das erste, eine inhärent globale Definition und das zweite, eine lokale statische.
JRobert

-3

In der Dokumentation von Atmel heißt es: "Wenn eine globale Variable deklariert wird, wird dieser Variablen zur Zeit der Programmverknüpfung eine eindeutige Adresse im SRAM zugewiesen."

Die vollständige Dokumentation finden Sie hier (Tipp 2 für globale Variablen): http://www.atmel.com/images/doc8453.pdf


4
Werden nicht beide Beispiele eine eindeutige Adresse in SRAM haben? Sie müssen beide bestehen bleiben.
Cybergibbons

2
Ja, tatsächlich finden Sie diese Informationen im selben Dokument in Tipp 6
jfpoilpret
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.