Saya relatif baru dalam pemrograman dan banyak praktik pengkodean terbaik yang saya baca secara efektif menyatakan bahwa ada beberapa alasan bagus untuk menggunakan variabel global (atau bahwa kode terbaik tidak memiliki global sama sekali).
Saya sudah melakukan yang terbaik untuk mengingat hal ini, ketika menulis perangkat lunak untuk membuat antarmuka Arduino dengan kartu SD, berbicara dengan komputer dan menjalankan pengontrol motor.
Saat ini saya memiliki 46 global untuk sekitar 1100 baris kode "tingkat pemula," (tidak ada garis yang memiliki lebih dari satu tindakan). Apakah ini rasio yang baik atau haruskah saya melihat mengurangi lebih banyak? Juga praktik apa yang dapat saya terapkan untuk mengurangi jumlah global lebih lanjut?
Saya menanyakan hal ini di sini karena saya secara khusus memusatkan perhatian pada praktik terbaik untuk pengkodean pada produk Arduino daripada pemrograman komputer pada umumnya.
sumber
Jawaban:
Mereka tidak jahat sendiri, tetapi mereka cenderung terlalu sering digunakan di mana tidak ada alasan yang baik untuk menggunakannya.
Ada banyak waktu ketika variabel global menguntungkan. Terutama karena pemrograman Arduino, di bawah tenda, sangat berbeda dengan pemrograman PC.
Manfaat terbesar untuk variabel global adalah alokasi statis. Terutama dengan variabel besar dan kompleks seperti instance kelas. Alokasi dinamis (penggunaan
new
dll) disukai karena kurangnya sumber daya.Anda juga tidak mendapatkan pohon panggilan tunggal seperti yang Anda lakukan dalam program C normal (
main()
fungsi tunggal memanggil fungsi lain) - sebagai gantinya Anda secara efektif mendapatkan dua pohon terpisah (setup()
fungsi panggilan, laluloop()
fungsi panggilan), yang berarti bahwa kadang-kadang variabel global adalah satu-satunya cara untuk mencapai tujuan Anda (yaitu, jika Anda ingin menggunakannya di keduanyasetup()
danloop()
).Jadi tidak, mereka tidak jahat, dan pada Arduino mereka memiliki kegunaan yang lebih banyak dan lebih baik daripada di PC.
sumber
loop()
(atau dalam beberapa fungsi dipanggilloop()
)? apakah lebih baik mengaturnya dengan cara yang berbeda daripada mendefinisikannya di awal?static
olah saya membutuhkan mereka untuk mempertahankan nilai mereka di iterasi) dan meneruskannya melalui parameter fungsi ke rantai panggilan.void foo(int &var) { var = 4; }
danfoo(n);
-n
sekarang 4.Sangat sulit untuk memberikan jawaban yang pasti tanpa melihat kode Anda yang sebenarnya.
Variabel global tidak jahat, dan mereka sering masuk akal di lingkungan tertanam di mana Anda biasanya melakukan banyak akses perangkat keras. Anda hanya memiliki empat UART, hanya satu port I2C, dll. Jadi masuk akal untuk menggunakan global untuk variabel yang terkait dengan sumber daya perangkat keras tertentu. Dan memang, perpustakaan inti Arduino melakukan itu:
Serial
,Serial1
, dll adalah variabel global. Juga, variabel yang mewakili keadaan global dari program biasanya adalah global.Bukan tentang angka. Pertanyaan yang tepat yang harus Anda tanyakan pada diri Anda adalah, untuk masing-masing global ini, apakah masuk akal untuk memilikinya dalam lingkup global.
Namun, 46 global tampaknya agak tinggi bagi saya. Jika beberapa di antaranya memiliki nilai konstan, kualifikasi sebagai
const
: kompiler biasanya akan mengoptimalkan penyimpanannya. Jika salah satu dari variabel tersebut hanya digunakan di dalam satu fungsi, buatlah lokal. Jika Anda ingin nilainya tetap ada di antara panggilan ke fungsi, kualifikasi sebagaistatic
. Anda juga bisa mengurangi jumlah global "terlihat" dengan mengelompokkan variabel bersama di dalam suatu kelas, dan memiliki satu instance global dari kelas ini. Tetapi hanya melakukannya ketika masuk akal untuk menyatukan barang-barang. MembuatGlobalStuff
kelas besar demi hanya memiliki satu variabel global tidak akan membantu membuat kode Anda lebih jelas.sumber
static
apakah saya memiliki variabel yang hanya digunakan dalam satu fungsi tetapi mendapatkan nilai baru setiap kali fungsi dipanggil (sepertivar=millis()
) haruskah saya membuatnyastatic
?static
Hanya untuk saat Anda perlu menyimpan nilainya. Jika hal pertama yang Anda lakukan dalam fungsi adalah mengatur nilai variabel ke waktu saat ini, tidak perlu menyimpan nilai lama. Semua yang statis lakukan dalam situasi itu adalah membuang-buang memori.Masalah utama dengan variabel global adalah pemeliharaan kode. Saat membaca sebaris kode, mudah untuk menemukan deklarasi variabel yang dilewatkan sebagai parameter atau dideklarasikan secara lokal. Tidak mudah untuk menemukan deklarasi variabel global (seringkali diperlukan dan IDE).
Ketika Anda memiliki banyak variabel global (40 sudah banyak), menjadi sulit untuk memiliki nama eksplisit yang tidak terlalu panjang. Menggunakan namespace adalah cara untuk memperjelas peran variabel global.
Cara yang buruk untuk meniru ruang nama di C adalah:
Pada prosesor intel atau lengan, akses ke variabel global lebih lambat daripada variabel lain. Ini mungkin kebalikan dari arduino.
sumber
Meskipun saya tidak akan menggunakannya saat pemrograman untuk PC, untuk Arduino mereka memiliki beberapa manfaat. Sebagian besar jika sudah dikatakan:
Juga, dalam beberapa kasus, terutama dari segi kinerja, sebaiknya menggunakan variabel global, alih-alih membuat elemen saat dibutuhkan:
sumber
Seperti halnya segala sesuatu (selain goto yang benar-benar jahat) global memiliki tempat mereka.
mis. Jika Anda memiliki port serial debug atau file log yang Anda perlukan untuk dapat menulis dari mana saja, maka seringkali masuk akal untuk membuatnya menjadi global. Demikian pula jika Anda memiliki informasi penting tentang status sistem maka menjadikannya global sering merupakan solusi termudah. Tidak ada gunanya memiliki nilai yang harus Anda berikan ke setiap fungsi tunggal dalam program.
Seperti yang dikatakan orang lain, 46 sepertinya hanya lebih dari 1000 baris kode tetapi tanpa mengetahui detail dari apa yang Anda lakukan, sulit untuk mengatakan apakah Anda menggunakannya berlebihan atau tidak.
Namun untuk setiap global, tanyakan pada diri sendiri beberapa pertanyaan penting:
Apakah namanya jelas dan spesifik sehingga saya tidak sengaja mencoba menggunakan nama yang sama di tempat lain? Jika tidak ganti nama.
Apakah ini perlu diubah? Jika tidak maka pertimbangkan untuk menjadikannya const.
Apakah ini perlu terlihat di mana-mana atau hanya global agar nilainya dipertahankan antara panggilan fungsi? Jadi pertimbangkan membuatnya lokal ke fungsi dan menggunakan kata kunci statis.
Akankah hal-hal benar-benar kacau jika ini diubah oleh sepotong kode ketika saya tidak berhati-hati? mis. jika Anda memiliki dua variabel terkait, ucapkan nama dan nomor ID, yang bersifat global (lihat catatan sebelumnya tentang penggunaan global ketika Anda membutuhkan beberapa informasi hampir di mana-mana) maka jika salah satu dari mereka diubah tanpa hal buruk lainnya dapat terjadi. Ya, Anda bisa saja berhati-hati tetapi kadang-kadang ada baiknya sedikit menegakkan hati. mis. meletakkannya dalam file .c yang berbeda dan kemudian mendefinisikan fungsi yang mengatur keduanya sekaligus dan memungkinkan Anda untuk membacanya. Anda kemudian hanya memasukkan fungsi-fungsi dalam file header terkait, dengan cara itu sisa kode Anda hanya dapat mengakses variabel melalui fungsi yang ditentukan dan sehingga tidak dapat mengacaukan semuanya.
- Perbarui - Saya baru menyadari bahwa Anda telah bertanya tentang praktik terbaik khusus Arduino daripada pengkodean umum dan ini lebih merupakan balasan kode umum. Tetapi jujur tidak ada banyak perbedaan, praktik yang baik adalah praktik yang baik. The
startup()
danloop()
struktur Arduino berarti bahwa Anda harus menggunakan GLOBALS sedikit lebih dari platform lain dalam beberapa situasi tapi itu tidak benar-benar berubah banyak, Anda selalu berakhir bertujuan untuk yang terbaik yang dapat Anda lakukan dalam keterbatasan platform tidak peduli apa platform ini.sumber
goto
s dan itu untuk keluar dari loop bersarang, itu jauh lebih bersih dan lebih mudah dipahami daripada alternatif.goto
itu jahat, periksa kode Linux. Bisa dibilang, mereka sama jahatnya sepertitry...catch
balok ketika digunakan seperti itu.Apakah mereka jahat? Mungkin. Masalah dengan global adalah bahwa mereka dapat diakses dan dimodifikasi pada suatu titik waktu dengan fungsi atau bagian kode apa pun yang dieksekusi, tanpa batasan. Ini dapat menyebabkan situasi yang, katakanlah, sulit untuk dilacak dan dijelaskan. Oleh karena itu diminimalkan jumlah global, jika mungkin membawa jumlah kembali ke nol, karena itu diinginkan.
Bisakah mereka dihindari? Hampir selalu ya. Masalah dengan Arduino adalah, bahwa mereka memaksa Anda ke dalam dua pendekatan fungsi di mana mereka menganggap Anda
setup()
dan Andaloop()
. Dalam kasus khusus ini Anda tidak memiliki akses ke ruang lingkup fungsi pemanggil dari kedua fungsi ini (mungkinmain()
). Jika Anda melakukannya, Anda akan dapat menyingkirkan diri Anda dari semua global dan menggunakan penduduk lokal sebagai gantinya.Bayangkan yang berikut ini:
Ini mungkin lebih atau kurang apa fungsi utama dari sebuah penampilan Program Arduino seperti. Variabel yang Anda butuhkan di kedua
setup()
danloop()
fungsi akan kemudian lebih dinyatakan dalam lingkupmain()
fungsi daripada lingkup global. Mereka kemudian dapat diakses oleh dua fungsi lain dengan cara melewati mereka sebagai argumen (menggunakan pointer jika diperlukan).Sebagai contoh:
Perhatikan bahwa dalam hal ini Anda juga perlu mengubah tanda tangan dari kedua fungsi tersebut.
Karena ini mungkin tidak layak atau tidak diinginkan, saya benar-benar melihat hanya satu cara untuk menghapus sebagian besar global dari program Arduino tanpa memodifikasi struktur program yang dipaksakan.
Jika saya ingat dengan benar, Anda dapat menggunakan C ++ saat pemrograman untuk Arduino, dan bukan C. Jika Anda belum terbiasa dengan OOP (Pemrograman Berorientasi Objek) atau C ++, mungkin perlu beberapa waktu untuk membiasakan diri dan beberapa bacaan.
Proposal saya akan membuat kelas Program dan membuat contoh tunggal global kelas ini. Kelas harus dianggap sebagai cetak biru untuk objek.
Pertimbangkan contoh program berikut:
Voa, kami telah menyingkirkan hampir semua global. Fungsi di mana Anda akan mulai menambahkan logika aplikasi Anda akan menjadi
Program::setup()
danProgram::loop()
fungsi. Fungsi-fungsi ini memiliki akses ke variabel anggota contoh spesifikmyFirstSampleVariable
danmySecondSampleVariable
sedangkan fungsi tradisionalsetup()
danloop()
tidak memiliki akses karena variabel-variabel ini telah ditandai sebagai kelas privat. Konsep ini disebut enkapsulasi data atau penyembunyian data.Mengajari Anda OOP dan / atau C ++ sedikit keluar dari ruang lingkup jawaban untuk pertanyaan ini jadi saya akan berhenti di sini.
Untuk meringkas: global harus dihindari dan hampir selalu mungkin untuk secara drastis mengurangi jumlah global. Juga saat Anda pemrograman untuk Arduino.
Yang terpenting saya harap jawaban saya agak berguna bagi Anda :)
sumber
Program::instance().setup()
bukanglobalProgram.setup()
. Menempatkan variabel global terkait ke dalam satu kelas / struct / namespace dapat bermanfaat, terutama jika mereka hanya dibutuhkan oleh beberapa fungsi terkait, tetapi pola tunggal tidak menambahkan apa-apa. Dengan kata lainstatic Program p;
memiliki penyimpanan global danstatic Program& instance()
memiliki akses global, yang jumlahnya sama dengan sederhanaProgram globalProgram;
.Variabel global tidak pernah jahat . Aturan umum yang menentang mereka hanyalah penopang agar Anda bertahan cukup lama untuk mendapatkan pengalaman membuat keputusan yang lebih baik.
Apa variabel global, adalah asumsi yang melekat bahwa hanya ada satu hal (tidak masalah jika kita berbicara tentang array global atau peta yang mungkin mengandung banyak hal, yang masih mengandung asumsi bahwa hanya ada satu daftar atau pemetaan seperti itu, dan bukan beberapa yang independen).
Jadi sebelum Anda menggunakan global, Anda ingin bertanya pada diri sendiri: apakah mungkin saya ingin menggunakan lebih dari satu hal ini? Jika ternyata benar, Anda harus memodifikasi kode untuk membatalkan globalisasi hal itu, dan Anda mungkin akan mengetahui sepanjang bagian bahwa kode Anda bergantung pada asumsi keunikan, sehingga Anda Harus memperbaikinya juga, dan prosesnya menjadi membosankan dan rawan kesalahan. "Jangan gunakan global" diajarkan karena biasanya itu adalah biaya yang cukup kecil untuk menghindari global sejak awal, dan itu menghindari potensi harus membayar biaya besar di kemudian hari.
Tetapi asumsi penyederhanaan yang memungkinkan global juga membuat kode Anda lebih kecil, lebih cepat, dan menggunakan lebih sedikit memori, karena tidak harus menyampaikan gagasan tentang hal mana yang digunakan, tidak harus melakukan tipuan, tidak harus pertimbangkan kemungkinan hal yang diinginkan mungkin tidak ada, dll. Pada embedded Anda lebih cenderung dibatasi untuk ukuran kode dan / atau waktu CPU dan / atau memori daripada Anda di PC, jadi penghematan ini bisa berarti. Dan banyak aplikasi tertanam juga memiliki lebih banyak kekakuan dalam persyaratan - Anda tahu bahwa chip Anda hanya memiliki salah satu periferal tertentu, pengguna tidak bisa begitu saja mencolokkan yang lain ke port USB atau sesuatu.
Alasan umum lainnya untuk menginginkan lebih dari satu dari sesuatu yang tampaknya unik adalah pengujian - menguji interaksi antara dua komponen lebih mudah ketika Anda bisa memberikan contoh pengujian beberapa komponen ke suatu fungsi, sedangkan mencoba untuk memodifikasi perilaku komponen global adalah proposisi yang lebih rumit. Tetapi pengujian di dunia yang disematkan cenderung sangat berbeda dari tempat lain, jadi ini mungkin tidak berlaku untuk Anda. Sejauh yang saya tahu, Arduino tidak memiliki budaya ujian apa pun.
Jadi, teruskan dan gunakan global ketika mereka tampak berharga. Polisi kode tidak akan datang dan menjemputmu. Hanya tahu bahwa pilihan yang salah dapat menyebabkan lebih banyak pekerjaan untuk Anda, jadi jika Anda tidak yakin ...
sumber
tidak ada yang secara inheren jahat, termasuk variabel global. Saya akan menganggapnya sebagai "kejahatan yang diperlukan" - itu bisa membuat hidup Anda lebih mudah tetapi harus didekati dengan hati-hati.
gunakan fungsi wrapper untuk mengakses variabel global Anda. jadi Anda setidaknya mengelola dari perspektif ruang lingkup.
sumber