Qual è la differenza tra dichiarare una variabile al di fuori del ciclo e dichiarare il ciclo interno statico?


9

Questi sono due modi in cui posso contenere una variabile al di fuori del ciclo (o di qualsiasi funzione).

Innanzitutto, posso dichiararlo con ambito globale al di fuori del ciclo:

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

int count = 0;

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

    delay(250);
}

Posso anche dichiararlo statico all'interno del ciclo:

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

void loop()
{
    static int count = 0;

    Serial.println(count);
    count++;

    delay(250);
}

Che differenza farà questa?

Risposte:


10

La differenza di base è di portata.

Nel primo caso, stai dichiarando una variabile globale. È una variabile accessibile in ogni ambito dopo la sua definizione.

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

Nel secondo caso, stai dichiarando una variabile statica con ambito locale. La variabile persisterà per l'intero programma eseguito in modo simile alle variabili globali, ma sarà accessibile solo nel blocco di codice in cui è dichiarata. Questo è lo stesso esempio, con una sola modifica. countè ora dichiarato come variabile statica all'interno loop.

void inc();

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

    inc();

    delay(500);
}

Questo non verrà compilato in quanto la funzione inc()non ha accesso count.

Le variabili globali, per quanto apparentemente utili, presentano alcune insidie. Questi possono persino causare danni quando si tratta di scrivere programmi che possono interagire con l'ambiente fisico. Questo è un esempio molto semplice di qualcosa che è molto probabile che accada, non appena i programmi iniziano a diventare più grandi. Una funzione può inavvertitamente modificare lo stato di una variabile 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;

}

È molto difficile eseguire il debug di questi casi. Questo tipo di problema, tuttavia, può essere facilmente rilevato semplicemente usando una variabile statica.

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

Dal punto di vista funzionale, entrambe le versioni generano lo stesso risultato, poiché in entrambi i casi il valore di countviene memorizzato tra le esecuzioni di loop()(o perché è una variabile globale o perché è contrassegnato come statice quindi mantiene il suo valore).

Quindi la decisione che scegliere scende ai seguenti argomenti:

  1. In genere, nell'informatica, è incoraggiato a mantenere le variabili il più possibile locali in termini di portata . Questo di solito si traduce in un codice molto più chiaro con meno effetti collaterali e riduce le possibilità che qualcun altro usi quella variabile globale che rovina la tua logica). Ad esempio nel tuo primo esempio, altre aree logiche potrebbero cambiare il countvalore, mentre nel secondo solo quella particolare funzione loop()può farlo).
  2. Le variabili globali e statiche occupano sempre memoria , dove fanno i locali solo quando si trovano nell'ambito. Nei tuoi esempi precedenti ciò non fa alcuna differenza (poiché in uno usi una variabile globale, nell'altro una variabile statica), ma in programmi più grandi e più complessi potrebbe e potresti risparmiare memoria usando i locali non statici. Tuttavia : se si dispone di una variabile in un'area logica che viene eseguita molto spesso, prendere in considerazione la possibilità di renderla statica o globale, poiché altrimenti si perde un po 'di prestazioni ogni volta che si entra in quell'area logica, poiché ci vuole un po' di tempo per alloca la memoria per quella nuova istanza variabile. È necessario trovare un equilibrio tra carico di memoria e prestazioni.
  3. Altri punti come una migliore disposizione per l' analisi statica o l' ottimizzazione da parte del compilatore potrebbero anche entrare in gioco.
  4. In alcuni scenari speciali, potrebbero esserci problemi con l'ordine di inizializzazione imprevedibile degli elementi statici (non sono sicuro di quel punto, confronta questo link però).

Fonte: thread simile su arduino.cc


Il rientro non dovrebbe mai essere un problema su Arduino poiché non supporta la concorrenza.
Peter Bloomfield

Vero. Questo era più un punto generale, ma in effetti non rilevante per Arduino. Ho rimosso quel pezzo.
Philip Allgaier

1
Una variabile statica dichiarata all'interno di un ambito esisterà sempre e utilizzerà lo stesso spazio di una variabile globale! Nel codice OP, l'unica differenza è quale codice può accedere alla variabile. In scipe static sarà accessibile all'interno dello stesso ambito.
jfpoilpret

1
@jfpoilpret Questo ovviamente è vero e vedo che la rispettiva parte nella mia risposta è stata un po 'fuorviante. Risolto questo.
Philip Allgaier,

2

Entrambe le variabili sono statiche e persistono per l'intera sessione di esecuzione. Il globale è visibile a qualsiasi funzione se dichiara - non definisce - il globale o se la funzione segue la definizione nella stessa unità di compilazione (file + include).

Spostare la definizione countall'interno di una funzione limita il suo ambito di visibilità al set di {}es racchiuso più vicino e gli conferisce la durata dell'invocazione della funzione (viene creata e distrutta quando la funzione viene inserita ed chiusa). La sua dichiarazione staticdà anche la durata della sessione di esecuzione che esiste dall'inizio alla fine della sessione di esecuzione, persistendo nelle invocazioni di funzioni.

A proposito: sii cauto sull'uso della statica inizializzata all'interno di una funzione, poiché ho visto alcune versioni del compilatore gnu sbagliare. Una variabile automatica con un inizializzatore deve essere creata e inizializzata su ogni voce della funzione. Uno statico con un inizializzatore dovrebbe essere inizializzato solo una volta, durante la configurazione dell'esecuzione, prima che a main () venga dato il controllo (proprio come sarebbe un globale). Ho avuto la reinizializzazione della statica locale su ogni voce della funzione come se fossero automatici, il che non è corretto. Prova il tuo compilatore per essere sicuro.


Non sono sicuro di capire cosa intendi per una funzione che dichiara un globale. Intendi come extern?
Peter Bloomfield,

@ PeterR.Bloomfield: Non sono sicuro di quale parte del mio post mi stai chiedendo, ma mi riferivo ai due esempi del PO: il primo, una definizione intrinsecamente globale e il secondo, una statica locale.
JRobert

-3

Secondo la documentazione di Atmel: "Se viene dichiarata una variabile globale, un indirizzo univoco nella SRAM verrà assegnato a questa variabile al momento del collegamento al programma."

La documentazione completa è qui (Suggerimento n. 2 per le variabili globali): http://www.atmel.com/images/doc8453.pdf


4
Entrambi gli esempi non finiranno con un indirizzo univoco in SRAM? Entrambi devono persistere.
Cybergibbons

2
Sì, in realtà puoi trovare quelle informazioni nello stesso documento nel suggerimento n. 6
jfpoilpret del
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.