Quelle est la différence entre déclarer une variable à l'extérieur de la boucle et déclarer une boucle statique à l'intérieur?

9

Ce sont deux façons dont je peux tenir une variable en dehors de la boucle (ou de n'importe quelle fonction).

Tout d'abord, je peux le déclarer avec une portée globale en dehors de la boucle:

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

int count = 0;

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

    delay(250);
}

Je peux également le déclarer statique dans la boucle:

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

void loop()
{
    static int count = 0;

    Serial.println(count);
    count++;

    delay(250);
}

Quelle différence, le cas échéant, cela fera-t-il?

Cybergibbons
la source

Réponses:

10

La différence la plus fondamentale concerne la portée.

Dans le premier cas, vous déclarez une variable globale. Il s'agit d'une variable accessible dans toutes les étendues après sa définition.

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;
};

Dans le second cas, vous déclarez une variable statique avec une portée locale. La variable persistera pour l'ensemble du programme exécuté de la même manière que les variables globales, mais ne sera accessible que dans le bloc de code dans lequel elle est déclarée. Il s'agit du même exemple, avec une seule modification. countest maintenant déclaré comme une variable statique à l'intérieur loop.

void inc();

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

    inc();

    delay(500);
}

Cela ne sera pas compilé car la fonction inc()n'y a pas accès count.

Les variables globales, bien qu'apparemment utiles, comportent certains écueils. Ceux-ci peuvent même causer des dommages lorsqu'il s'agit d'écrire des programmes qui peuvent interagir avec l'environnement physique. Ceci est un exemple très basique de quelque chose qui est susceptible de se produire, dès que les programmes commencent à s'agrandir. Une fonction peut modifier par inadvertance l'état d'une variable globale.

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;

}

De tels cas sont très difficiles à déboguer. Ce type de problème peut cependant être facilement détecté, simplement en utilisant une variable statique.

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;

}
asheeshr
la source
5

D'un point de vue fonctionnel, les deux versions génèrent le même résultat, car dans les deux cas, la valeur de countest stockée entre les exécutions de loop()(soit parce qu'il s'agit d'une variable globale, soit parce qu'elle est marquée comme staticet conserve donc sa valeur).

Ainsi, la décision de choisir se résume aux arguments suivants:

  1. Généralement, en informatique, il est conseillé de garder vos variables aussi locales que possible en termes de portée . Cela se traduit généralement par un code beaucoup plus clair avec moins d'effets secondaires et réduit les chances que quelqu'un d'autre utilise cette variable globale en vissant votre logique). Par exemple, dans votre premier exemple, d'autres zones logiques peuvent modifier la countvaleur, tandis que dans le second, seule cette fonction particulière loop()peut le faire).
  2. Les variables globales et statiques occupent toujours la mémoire , alors que les locaux ne le font que lorsqu'ils sont dans la portée. Dans vos exemples ci-dessus, cela ne fait aucune différence (car dans l'un, vous utilisez une variable globale, dans l'autre une variable statique), mais dans des programmes plus grands et plus complexes, cela pourrait et vous pourriez économiser de la mémoire en utilisant des sections locales non statiques. Cependant : si vous avez une variable dans une zone logique qui est exécutée très souvent, envisagez de la rendre statique ou globale, car sinon vous perdez un tout petit peu de performances chaque fois que cette zone logique est entrée, car cela prend un peu de temps pour allouer la mémoire pour cette nouvelle instance de variable. Vous devez trouver un équilibre entre la charge mémoire et les performances.
  3. D'autres points tels qu'une meilleure mise en page pour l'analyse statique ou l' optimisation par le compilateur pourraient également entrer en jeu.
  4. Dans certains scénarios spéciaux, il peut y avoir des problèmes avec l'ordre d'initialisation imprévisible des éléments statiques (vous n'êtes pas sûr de ce point, comparez cependant ce lien ).

Source: fil similaire sur arduino.cc

Philip Allgaier
la source
La ré-entrée ne devrait jamais être un problème sur Arduino car il ne prend pas en charge la concurrence.
Peter Bloomfield
Vrai. C'était plus un point général, mais en fait pas pertinent pour Arduino. J'ai enlevé ce morceau.
Philip Allgaier
1
Une variable statique déclarée à l'intérieur d'une portée existera toujours et utilisera le même espace qu'une variable globale! Dans le code OP, la seule différence est quel code peut accéder à la variable. In scipe static sera accessible dans la même portée.
jfpoilpret
1
@jfpoilpret C'est bien sûr vrai, et je vois que la partie respective de ma réponse était un peu trompeuse. Correction de ça.
Philip Allgaier
2

Les deux variables sont statiques - elles persistent pendant toute la session d'exécution. Le global est visible par n'importe quelle fonction s'il déclare - et non définit - le global, ou si la fonction suit la définition dans la même unité de compilation (fichier + inclut).

Le déplacement de la définition de countà l'intérieur d'une fonction à la fois limite son champ de visibilité au jeu d' {}es englobant le plus proche et lui confère une durée de vie d'invocation de fonction (elle est créée et détruite lorsque la fonction est entrée et sortie). Le déclarer lui staticdonne également la durée de vie de la session d'exécution, il existe du début à la fin de la session d'exécution, persistant à travers les appels de fonction.

BTW: soyez prudent sur l'utilisation de la statique initialisée dans une fonction, car j'ai vu certaines versions du compilateur gnu se tromper. Une variable automatique avec un initialiseur doit être créée et initialisée à chaque entrée de fonction. Un statique avec un initialiseur ne doit être initialisé qu'une seule fois, pendant la configuration de l'exécution, avant que main () ne reçoive le contrôle (tout comme le serait un global). J'ai fait réinitialiser la statique locale sur chaque entrée de fonction comme s'il s'agissait d'automatique, ce qui est incorrect. Testez votre propre compilateur pour en être sûr.

JRobert
la source
Je ne suis pas sûr de comprendre ce que vous entendez par une fonction déclarant un global. Voulez-vous dire en tant que extern?
Peter Bloomfield
@ PeterR.Bloomfield: Je ne sais pas de quelle partie de mon message vous parlez, mais je faisais référence aux deux exemples du PO - le premier, une définition intrinsèquement globale et le second, une statique locale.
JRobert
-3

Selon la documentation d'Atmel: "Si une variable globale est déclarée, une adresse unique dans la SRAM sera attribuée à cette variable au moment de la liaison du programme."

La documentation complète est ici (Astuce # 2 pour les variables globales): http://www.atmel.com/images/doc8453.pdf

Void Main
la source
4
Les deux exemples ne vont-ils pas aboutir à une adresse unique dans SRAM? Ils doivent tous deux persister.
Cybergibbons
2
Oui, en fait, vous pouvez trouver cette information dans le même document dans le conseil n ° 6
jfpoilpret