Apa perbedaan antara mendeklarasikan variabel di luar loop dan mendeklarasikan static inside loop?

9

Ini adalah dua cara saya bisa memegang variabel di luar loop (atau fungsi apa pun).

Pertama, saya bisa mendeklarasikannya dengan lingkup global di luar loop:

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

int count = 0;

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

    delay(250);
}

Saya juga dapat mendeklarasikannya statis di dalam loop:

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

void loop()
{
    static int count = 0;

    Serial.println(count);
    count++;

    delay(250);
}

Apa perbedaan, jika ada, apakah ini akan membuat?

Cybergibbons
sumber

Jawaban:

10

Perbedaan paling mendasar adalah ruang lingkup.

Dalam kasus pertama, Anda mendeklarasikan variabel global. Ini adalah variabel yang dapat diakses di setiap lingkup setelah definisinya.

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

Dalam kasus kedua, Anda mendeklarasikan variabel statis dengan cakupan lokal. Variabel akan bertahan selama seluruh program berjalan mirip dengan variabel global, tetapi hanya dapat diakses di blok kode yang dideklarasikan. Ini adalah contoh yang sama, dengan hanya satu perubahan. countsekarang dinyatakan sebagai variabel statis di dalamnya loop.

void inc();

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

    inc();

    delay(500);
}

Ini tidak akan dikompilasi karena fungsi inc()tidak memiliki akses count.

Variabel global, betapapun bermanfaat, datang dengan beberapa jebakan. Ini bahkan dapat menyebabkan kerusakan ketika datang ke menulis program yang dapat berinteraksi dengan lingkungan fisik. Ini contoh yang sangat mendasar dari sesuatu yang sangat mungkin terjadi, segera setelah program mulai membesar. Suatu fungsi dapat secara tidak sengaja mengubah status variabel global.

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;

}

Kasus-kasus seperti itu sangat sulit di-debug. Namun jenis masalah ini, dapat dengan mudah dideteksi, hanya dengan menggunakan variabel statis.

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
sumber
5

Dari perspektif fungsional, kedua versi menghasilkan hasil yang sama, karena dalam kedua kasus nilai countdisimpan di antara eksekusi loop()(baik karena itu adalah variabel global atau karena itu ditandai sebagai staticdan karena itu menjaga nilainya).

Jadi keputusan yang harus dipilih datang ke argumen berikut:

  1. Secara umum, dalam ilmu komputer, disarankan untuk menjaga agar variabel Anda tetap bersifat lokal dalam hal cakupan . Ini biasanya menghasilkan kode yang jauh lebih jelas dengan efek samping yang lebih sedikit dan mengurangi kemungkinan orang lain menggunakan variabel global itu mengacaukan logika Anda). Misalnya dalam contoh pertama Anda, area logika lain dapat mengubah countnilai, sedangkan pada yang kedua, hanya fungsi tertentu yang loop()dapat melakukannya).
  2. Variabel global dan statis selalu menempati memori , sedangkan penduduk lokal hanya melakukannya ketika mereka berada dalam ruang lingkup. Dalam contoh di atas Anda yang tidak membuat perbedaan (karena di satu Anda menggunakan global, yang lain variabel statis), tetapi dalam program yang lebih besar dan lebih kompleks itu mungkin dan Anda bisa menghemat memori menggunakan lokal non-statis. Namun : Jika Anda memiliki variabel di area logika yang dieksekusi sangat sering, pertimbangkan membuatnya menjadi statis atau global, karena jika tidak, Anda kehilangan sedikit kinerja setiap kali area logika dimasukkan, karena dibutuhkan sedikit waktu untuk mengalokasikan memori untuk instance variabel baru itu. Anda perlu menemukan keseimbangan antara beban memori dan kinerja.
  3. Poin lain seperti tata letak yang lebih baik untuk analisis statis atau optimisasi oleh kompiler mungkin juga ikut berperan.
  4. Dalam beberapa skenario khusus, mungkin ada masalah dengan urutan inisialisasi elemen statis yang tidak dapat diprediksi (tidak yakin tentang hal itu, bandingkan tautan ini ).

Sumber: Utas serupa di arduino.cc

Philip Allgaier
sumber
Re-entrancy tidak boleh menjadi masalah pada Arduino karena tidak mendukung konkurensi.
Peter Bloomfield
Benar. Itu lebih merupakan poin umum, tetapi memang tidak relevan untuk Arduino. Saya menghapus sedikit itu.
Philip Allgaier
1
Variabel statis yang dinyatakan di dalam lingkup akan selalu ada dan menggunakan ruang yang sama dengan variabel global! Dalam kode OP, satu-satunya perbedaan adalah kode apa yang dapat mengakses variabel. Dalam wipe statis akan dapat diakses dalam lingkup yang sama.
jfpoilpret
1
@ jfpoilpret Itu tentu saja benar, dan saya melihat bahwa masing-masing bagian dalam jawaban saya agak menyesatkan. Memperbaiki itu.
Philip Allgaier
2

Kedua variabel statis - mereka bertahan untuk seluruh sesi eksekusi. Global terlihat oleh fungsi apa pun jika ia menyatakan - bukan mendefinisikan - global, atau jika fungsi tersebut mengikuti definisi dalam unit kompilasi yang sama (file + termasuk).

Memindahkan definisi countke dalam suatu fungsi, keduanya membatasi cakupan visibilitasnya ke kumpulan {}es terdekat yang terlampir , dan memberikannya waktu pemanggilan fungsinya (fungsi tersebut dibuat dan dimusnahkan saat fungsi tersebut masuk dan keluar). Mendeklarasikannya staticjuga memberikannya sesi eksekusi seumur hidup dari awal hingga akhir sesi eksekusi, bertahan di seluruh pemanggilan fungsi.

BTW: hati-hati menggunakan statika diinisialisasi dalam suatu fungsi, karena saya telah melihat beberapa versi dari kompiler gnu mendapatkan kesalahan ini. Variabel otomatis dengan penginisialisasi harus dibuat dan diinisialisasi pada setiap entri fungsi. Statis dengan penginisialisasi hanya boleh diinisialisasi sekali, selama setup eksekusi, sebelum main () diberikan kontrol (seperti global akan). Saya memiliki statika lokal yang diinisialisasi ulang pada setiap entri fungsi seolah-olah itu adalah otomatis, yang tidak benar. Uji kompiler Anda sendiri untuk memastikan.

JRobert
sumber
Saya tidak yakin saya mengerti apa yang Anda maksud tentang suatu fungsi yang mendeklarasikan global. Apakah yang Anda maksud sebagai extern?
Peter Bloomfield
@ PeterR.Bloomfield: Saya tidak yakin bagian mana dari posting saya yang Anda tanyakan, tetapi saya merujuk pada dua contoh OP - yang pertama, definisi yang inheren global dan yang kedua, statis lokal.
JRobert
-3

Menurut dokumentasi Atmel: "Jika variabel global dideklarasikan, alamat unik dalam SRAM akan ditugaskan ke variabel ini pada waktu tautan program."

Dokumentasi lengkap ada di sini (Tip # 2 untuk variabel global): http://www.atmel.com/images/doc8453.pdf

Batal Utama
sumber
4
Bukankah kedua contoh akan berakhir dengan alamat unik di SRAM? Mereka berdua perlu bertahan.
Cybergibbons
2
Ya, sebenarnya Anda dapat menemukan info itu di dokumen yang sama di tip # 6
jfpoilpret