Mengapa menggunakan variabel int untuk pin ketika const int, enum atau #define jauh lebih masuk akal

24

Mengapa orang menggunakan variabel untuk menentukan nomor pin ketika pin tidak mungkin berubah sepanjang pelaksanaan kode?

Banyak kali saya melihat intmakhluk yang digunakan untuk definisi pin,

int led = 13;

saat penggunaan a const int

const int led = 13;

atau enum, atau#define

#define LED 13

jauh lebih masuk akal.

Bahkan dalam tutorial di situs Arduino, misalnya, tutorial pertama yang dijalankan kebanyakan orang, Blink .

Saya membaca suatu tempat yang const intlebih disukai #define. Mengapa ini tidak dianjurkan sejak awal, daripada membiarkan orang untuk mengembangkan kebiasaan buruk, sejak awal? Saya memperhatikannya beberapa waktu lalu, tetapi baru-baru ini mulai membuat saya jengkel, karena itu pertanyaannya.

Memory / pengolahan / komputasi bijaksana adalah const int, enum, atau dalam hal ini #define, lebih baik dari polos int, yaitu menempati kurang memori, disimpan dalam memori yang berbeda (Flash, EEPROM, SRAM), lebih cepat eksekusi, lebih cepat untuk mengkompilasi?


Ini mungkin merupakan duplikat dari Apakah lebih baik menggunakan #define atau konst? Int untuk konstanta? , tapi saya menjawab pertanyaan mengapa orang menggunakan variabel, dan bagaimana kinerja meningkat ketika mereka tidak, daripada jenis konstanta yang lebih baik.

Greenonline
sumber
9
Karena mengerikan melahirkan mengerikan. Kebanyakan penghobi bukan programmer yang berpengalaman dan karenanya mengajarkan kebiasaan buruk pada penghobi lainnya.
Ignacio Vazquez-Abrams
1
Khususnya dengan pin, bentuk sederhana dari fungsi arduino API dasar seperti digitalWrite tidak mendorong desain yang disematkan dengan benar, yaitu menggunakan masker dan satu alamat memori untuk seluruh port
crasic

Jawaban:

20
const int led = 13;

Itu adalah metode yang benar. Atau bahkan:

const byte led = 13;

Berapa pin yang Anda miliki?

Beberapa tutorial tidak cukup melalui kontrol kualitas sebanyak mungkin.

Performa akan lebih baik digunakan const byte, dibandingkan dengan intnamun kompiler mungkin cukup pintar untuk menyadari apa yang Anda lakukan.

Yang dapat Anda lakukan adalah dengan lembut mendorong orang untuk menggunakan teknik yang lebih efisien dengan menggunakannya dalam kode Anda sendiri.


Tanggapan terhadap komentar

  1. Seorang komentator telah menyarankan bahwa byteini bukan standar C. Ini benar, namun ini adalah situs Arduino StackExchange, dan saya percaya menggunakan tipe standar yang disediakan oleh Arduino IDE dapat diterima.

    Di Arduino.h ada baris ini:

    typedef uint8_t byte;

    Perhatikan bahwa ini tidak persis sama dengan unsigned char. Lihat uint8_t vs unsigned char dan When is uint8_t ≠ unsigned char? .

  2. Komentator lain menyarankan bahwa menggunakan byte tidak selalu meningkatkan kinerja, karena angka yang lebih kecil dari yang intakan dipromosikan int(lihat Aturan Promosi Integer jika Anda menginginkan lebih banyak tentang ini).

    Namun dalam konteks pengidentifikasi const , kompiler akan menghasilkan kode yang efisien dalam hal apa pun. Misalnya, membongkar "blink" memberikan ini dalam bentuk aslinya:

    00000086 <loop>:
      86:   8d e0           ldi r24, 0x0D   ; 13
      88:   61 e0           ldi r22, 0x01   ; 1
      8a:   1b d1           rcall   .+566       ; 0x2c2 <digitalWrite>

    Bahkan ia menghasilkan kode yang sama apakah 13:

    • Apakah harfiah
    • Adalah #define
    • Adalah const int
    • Adalah const byte

Kompiler tahu kapan ia bisa memasukkan nomor ke dalam satu register dan kapan tidak. Namun itu praktik yang baik untuk menggunakan pengkodean yang menunjukkan niat Anda . Memperjelas constbahwa angkanya tidak akan berubah, dan membuatnya byte(atau uint8_t) menjelaskan bahwa Anda mengharapkan sejumlah kecil.


Pesan kesalahan yang membingungkan

Alasan utama lain yang harus dihindari #defineadalah pesan kesalahan yang Anda dapatkan jika Anda melakukan kesalahan. Pertimbangkan sketsa "blink" yang memiliki kesalahan:

#define LED = 13;

void setup() {
  pinMode(LED, OUTPUT);      // <---- line with error
}

void loop() {
  digitalWrite(LED, HIGH);   // <---- line with error 
  delay(1000);             
  digitalWrite(LED, LOW);    // <---- line with error
  delay(1000);              
}

Di permukaan terlihat OK, tetapi menghasilkan pesan kesalahan ini:

Blink.ino: In function ‘void setup()’:
Blink:4: error: expected primary-expression before ‘=’ token
Blink:4: error: expected primary-expression before ‘,’ token
Blink:4: error: expected `;' before ‘)’ token
Blink.ino: In function ‘void loop()’:
Blink:8: error: expected primary-expression before ‘=’ token
Blink:8: error: expected primary-expression before ‘,’ token
Blink:8: error: expected `;' before ‘)’ token
Blink:10: error: expected primary-expression before ‘=’ token
Blink:10: error: expected primary-expression before ‘,’ token
Blink:10: error: expected `;' before ‘)’ token

Anda melihat garis yang disorot pertama (baris 4) dan bahkan tidak melihat simbol "=". Plus, garis terlihat baik-baik saja. Sekarang sudah cukup jelas apa masalahnya di sini ( = 13sedang diganti LED), tetapi ketika garis 400 baris lebih jauh dalam kode, tidak jelas masalahnya dengan cara LED didefinisikan.

Saya telah melihat orang jatuh untuk ini berkali-kali (termasuk saya).

Nick Gammon
sumber
Berapa pin yang Anda miliki? adalah poin yang sangat bagus Nick, karena sebagian besar papan hanya memiliki kisaran puluhan, bukan ratusan (yaitu lebih besar dari 255), jadi angka intyang berlebihan ... yaitu, sampai Arduino akhirnya keluar dengan papan Tera ... :-)
Greenonline
2
C tidak memiliki bytetipe . Maksudmu unsigned char.
Kevin
Kinerja tidak akan selalu lebih baik bytedaripada int, karena dalam sebagian besar konteks, nilai integer dengan tipe yang lebih kecil daripada intyang dipromosikan int.
Pete Becker
1
C doesn't have a byte type. You mean unsigned char.- Jawaban saya adalah dalam konteks Arduino, yang memiliki ini typedef uint8_t byte;. Jadi untuk Arduino, menggunakan bytetidak apa-apa.
Nick Gammon
Performance won't necessarily be better with byte instead of int- lihat pos yang diamandemen.
Nick Gammon
19

Seperti yang dinyatakan Ignacio dengan benar, pada dasarnya karena mereka tidak tahu yang lebih baik. Dan mereka tidak tahu yang lebih baik karena orang-orang yang mengajar mereka (atau sumber daya yang mereka gunakan saat belajar) tidak tahu yang lebih baik.

Sebagian besar kode dan tutorial Arduino ditulis oleh orang-orang yang tidak pernah memiliki pelatihan dalam pemrograman dan sangat "belajar sendiri" dari sumber daya oleh orang-orang yang sendiri banyak belajar sendiri tanpa pelatihan yang tepat dalam pemrograman.

Banyak cuplikan kode tutorial yang saya lihat di sekitar tempat (dan terutama yang hanya tersedia dalam video YouTube --- urgh) akan menjadi tanda gagal jika saya menandai mereka dalam ujian.

Ya, a constlebih disukai daripada non-const, dan bahkan lebih dari a #define, karena:

  • A const(seperti a #define, tidak seperti non-const) tidak mengalokasikan RAM apa pun
  • A const(seperti non-const, tetapi tidak seperti a #define) memberikan nilai tipe eksplisit

Poin kedua ada minat khusus. Kecuali jika secara khusus diceritakan sebaliknya dengan embedded type-casting ( (long)3) atau tipe suffix ( 3L) atau adanya titik desimal ( 3.0), #definesuatu bilangan akan selalu berupa bilangan bulat dan semua matematika yang dilakukan pada nilai tersebut akan seolah-olah merupakan bilangan bulat. Sebagian besar waktu itu bukan masalah, tetapi Anda bisa mengalami skenario menarik ketika Anda mencoba untuk #definenilai yang lebih besar dari yang bisa disimpan oleh integer, seperti #define COUNT 70000kemudian melakukan operasi matematika dengan intnilai - nilai lain di dalamnya. Dengan menggunakan constAnda bisa memberi tahu kompiler "Nilai ini harus diperlakukan sebagai tipe variabel ini" - jadi Anda akan menggunakan: const long count = 70000;dan semua akan bekerja seperti yang diharapkan.

Ini juga memiliki efek knock-on yang memeriksa jenis ketika melewati nilai di sekitar tempat itu. Cobalah meneruskan const longke fungsi yang mengharapkan intdan akan mengeluh tentang mempersempit rentang variabel (atau bahkan gagal untuk dikompilasi tergantung pada skenario). Lakukan itu dengan #definedan itu hanya akan terus memberikan hasil yang salah dan membuat Anda menggaruk kepala selama berjam-jam.

Majenko
sumber
7
Perlu dicatat bahwa constvariabel mungkin memerlukan RAM, tergantung pada konteksnya, misalnya jika itu diinisialisasi menggunakan nilai kembali dari fungsi non-constexpr.
Peter Bloomfield
Demikian pula, const int foo = 13; bar(&foo);pasti akan membutuhkan kompiler untuk mengalokasikan memori yang sebenarnya foo.
Ilmari Karonen
3
Jika Anda mendefinisikan makro yang memperluas ke nilai yang tidak cocok intdengan kompiler memperlakukan nilai memiliki tipe terkecil di mana ia akan cocok (modulo aturan tentang ditandatangani vs. tidak ditandatangani). Jika Anda menggunakan sistem dengan int16 bit, #define count 70000akan menghasilkan counttampilan seperti long, seperti jika telah didefinisikan sebagai const long count = 70000;. Lebih lanjut, jika Anda melewatkan salah satu versi tersebut countke fungsi yang diharapkan int, kompiler yang waras akan memperlakukannya sama.
Pete Becker
1
Saya setuju dengan @PeteBecker - konstruksi seperti #define COUNT 70000tidak memotong ke int, tetapi kompilator memperlakukannya sebagai tipe yang cukup besar untuk menampung nomor itu. Memang benar bahwa itu mungkin tidak jelas ketika Anda menggunakan COUNTitu bukan int, tetapi Anda bisa mengatakan hal yang sama tentang suatu cara const long.
Nick Gammon
2
"#define akan selalu menjadi bilangan bulat" Itu tidak benar. Anda mengambil aturan literal bilangan bulat dan menerapkannya ke makro preprosesor. Ini seperti membandingkan apel dan musik pop. Ekspresi COUNTdalam contoh Anda diganti sebelum kompilasi dengan ekspresi 70000, yang memiliki tipe yang didefinisikan oleh aturan literal, seperti 2atau 13Latau 4.0didefinisikan oleh aturan literal. Fakta bahwa Anda menggunakan #definealias untuk ekspresi itu tidak relevan. Anda dapat menggunakan #definealias potongan kode C yang sewenang-wenang, jika diinginkan.
Lightness Races dengan Monica
2

Sebagai pendatang baru 2 minggu di Arduino, saya memahami gagasan umum tentang Arduino yang diduduki oleh bukan pemrogram. Sebagian besar sketsa yang telah saya periksa, termasuk yang ada di situs Arduino, menunjukkan kurangnya pesanan, dengan sketsa yang tidak berfungsi & hampir tidak ada komentar yang masuk akal. Bagan alur tidak ada, dan "Perpustakaan" adalah campuran yang tidak dimoderasi.

Jim
sumber
0

Jawaban saya adalah ... mereka melakukannya karena berhasil. Saya mengalami kesulitan untuk tidak mengajukan pertanyaan dalam jawaban saya seperti "mengapa itu harus 'salah'?"

linhartr22
sumber
3
Salah satu ciri khas seorang programmer yang baik adalah bahwa kode selalu mencerminkan niat mereka.
Ignacio Vazquez-Abrams
1
Kami masih berbicara tentang Arduino, kan? ;)
linhartr22
3
Arduino sudah memiliki reputasi buruk di komunitas EE yang lebih besar karena desain perangkat keras yang biasa-biasa saja yang dikeluarkan oleh komunitas. Tidakkah seharusnya kita mencoba memberikan sesuatu tentang sesuatu ?
Ignacio Vazquez-Abrams
2
"Sebagian besar proyek tidak akan melibatkan risiko hidup atau keuangan ..." Tidak mengherankan di sana. Siapa yang ingin melibatkan Arduino di mana ada peluang risiko setelah melihat komunitas secara luas.
Ignacio Vazquez-Abrams
2
Itu 'salah' bukan karena itu tidak bekerja dalam satu situasi tertentu tetapi karena, dibandingkan dengan melakukannya 'benar,' ada lebih banyak situasi di mana itu tidak berhasil. Ini membuat kode rapuh; perubahan pada kode dapat menyebabkan kegagalan misterius yang memakan waktu debugging. Jenis memeriksa dan kesalahan pesan kompiler ada untuk membantu Anda menangkap kesalahan semacam itu lebih awal, bukan nanti.
Curt J. Sampson