Apa manfaat dari mengakhiri if… else if dibangun dengan klausa else?

137

Organisasi kami memiliki aturan pengkodean yang diperlukan (tanpa penjelasan apa pun) bahwa:

if… else if konstruksi harus diakhiri dengan klausa else

Contoh 1:

if ( x < 0 )
{
   x = 0;
} /* else not needed */

Contoh 2:

if ( x < 0 )
{
    x = 0;
}
else if ( y < 0 )
{
    x = 3;
}
else    /* this else clause is required, even if the */
{       /* programmer expects this will never be reached */
        /* no change in value of x */
}

Kasus tepi apa yang dirancang untuk ditangani ini?

Yang juga menjadi perhatian saya tentang alasannya adalah bahwa Contoh 1 tidak memerlukan elsetetapi Contoh 2 membutuhkannya . Jika alasannya adalah dapat digunakan kembali dan diperpanjang, saya pikir elseharus digunakan dalam kedua kasus tersebut.

Trevor
sumber
31
Mungkin tanya perusahaan Anda untuk alasan dan manfaatnya. Pada pandangan pertama, ini memaksa pemrogram untuk memikirkannya dan menambahkan komentar "tidak diperlukan tindakan". Alasan yang sama di balik (dan setidaknya sama kontroversialnya) pengecualian Java yang dicentang.
Thilo
32
Contoh 2 sebenarnya adalah contoh yang baik di mana sebuah assert(false, "should never go here")mungkin masuk akal
Thilo
2
Organisasi kami memiliki aturan yang serupa tetapi tidak sedetail ini. Tujuan dalam kasus kami ada dua. Pertama, konsistensi kode. Kedua, tidak ada string / keterbacaan yang longgar. Mewajibkan yang lain, bahkan saat tidak diperlukan, menambah kejelasan pada kode meskipun tidak didokumentasikan. Membutuhkan yang lain adalah sesuatu yang telah saya lakukan di masa lalu, bahkan saat tidak diperlukan, hanya untuk memastikan saya telah memperhitungkan semua kemungkinan hasil dalam aplikasi.
LuvnJesus
5
Anda selalu bisa mengalahkan jika dengan if (x < 0) { x = 0; } else { if (y < 0) { x = 3; }}. Atau Anda bisa saja mengikuti aturan seperti itu, banyak di antaranya bodoh, hanya karena Anda diharuskan.
Jim Balter
3
@Thilo Saya agak terlambat, tapi tetap saja tidak ada yang menangkap kesalahan: Tidak ada indikasi bahwa hal lain tidak boleh terjadi, hanya itu seharusnya tidak memiliki efek samping (yang tampaknya normal saat melakukan < 0pemeriksaan), sehingga assert akan untuk menghentikan program pada kasus yang mungkin paling umum terjadi di mana nilai berada dalam batas yang diharapkan.
Loduwijk

Jawaban:

150

Seperti disebutkan dalam jawaban lain, ini dari pedoman pengkodean MISRA-C. Tujuannya adalah pemrograman defensif, sebuah konsep yang sering digunakan dalam pemrograman kritis-misi.

Artinya, setiap if - else ifharus diakhiri dengan else, dan setiap switchharus diakhiri dengan a default.

Ada dua alasan untuk ini:

  • Kode yang mendokumentasikan sendiri. Jika Anda menulis elsetetapi membiarkannya kosong itu berarti: "Saya telah mempertimbangkan skenario ketika tidak ifdan juga tidak else ifbenar".

    Tidak menulis di elsesana berarti: "baik saya mempertimbangkan skenario di mana tidak ifjuga else ifbenar, atau saya benar-benar lupa untuk mempertimbangkannya dan berpotensi ada bug gemuk di sini dalam kode saya".

  • Hentikan kode pelarian. Dalam perangkat lunak misi kritis, Anda perlu menulis program yang kuat yang memperhitungkan bahkan untuk yang sangat tidak mungkin. Jadi Anda bisa melihat kode seperti

    if (mybool == TRUE) 
    {
    } 
    else if (mybool == FALSE) 
    {
    }
    else
    {
      // handle error
    }
    

    Kode ini akan benar-benar asing bagi pemrogram PC dan ilmuwan komputer, tetapi sangat masuk akal dalam perangkat lunak misi kritis, karena kode ini menangkap kasus di mana "mybool" telah rusak, untuk alasan apa pun.

    Secara historis, Anda akan takut kerusakan memori RAM karena EMI / noise. Ini tidak banyak menjadi masalah hari ini. Jauh lebih mungkin, kerusakan memori terjadi karena bug di tempat lain dalam kode: penunjuk ke lokasi yang salah, bug array-out-of-bounds, stack overflow, kode pelarian, dll.

    Jadi sebagian besar waktu, kode seperti ini kembali untuk menampar wajah Anda sendiri ketika Anda telah menulis bug selama tahap implementasi. Artinya, ini juga dapat digunakan sebagai teknik debug: program yang Anda tulis memberi tahu Anda saat Anda menulis bug.


EDIT

Mengenai mengapa elsetidak diperlukan setelah setiap if:

Sebuah if-elseatau if-else if-elseseluruhnya mencakup semua kemungkinan nilai yang dapat dimiliki variabel. Tetapi ifpernyataan sederhana tidak selalu ada untuk mencakup semua kemungkinan nilai, itu memiliki penggunaan yang jauh lebih luas. Paling sering Anda hanya ingin memeriksa kondisi tertentu dan jika tidak terpenuhi, maka jangan lakukan apa pun. Maka tidak ada artinya menulis program defensif untuk menutupi elsekasus ini.

Ditambah itu akan mengacaukan kode sepenuhnya jika Anda menulis kosong elsesetelah setiap if.

MISRA-C: 2012 15.7 tidak memberikan alasan mengapa elsetidak diperlukan, hanya menyatakan:

Catatan: elsepernyataan akhir tidak diperlukan untuk if pernyataan sederhana .

Lundin
sumber
6
Jika memori rusak, saya berharap itu merusak lebih dari sekadar mybool, termasuk mungkin kode pengecekan itu sendiri. Mengapa Anda tidak menambahkan blok lain yang memverifikasi bahwa Anda if/else if/elsedikompilasi sesuai dengan yang Anda harapkan? Dan satu lagi untuk memverifikasi pemverifikasi sebelumnya juga?
Oleg V. Volkov
49
Mendesah. Begini guys, jika Anda sama sekali tidak memiliki pengalaman lain selain pemrograman desktop, Anda tidak perlu membuat komentar sok tahu tentang sesuatu yang jelas tidak Anda alami. Ini selalu terjadi ketika pemrograman defensif dibahas dan pemrogram PC berhenti. Saya menambahkan komentar "Kode ini akan sepenuhnya asing bagi pemrogram PC" karena suatu alasan. Anda tidak memprogram perangkat lunak keamanan penting untuk dijalankan di komputer desktop berbasis RAM . Titik. Kode itu sendiri akan berada di flash ROM dengan checksum ECC dan / atau CRC.
Lundin
7
@Deduplicator Memang (kecuali myboolmemiliki tipe non-boolean, seperti yang terjadi sebelum C mendapatkannya sendiri bool; maka compiler tidak akan membuat asumsi tanpa analisis statis tambahan). Dan pada subjek 'Jika Anda menulis yang lain tetapi membiarkannya kosong, itu berarti: "Saya telah mempertimbangkan skenario ketika tidak jika atau yang lain jika benar".': Reaksi pertama saya adalah menganggap programmer lupa memasukkan kode blok else, jika tidak, mengapa blok else kosong hanya ada di sana? Sebuah // unusedkomentar akan sesuai, bukan hanya sebuah blok kosong.
JAB
6
@JAB Ya, elseblok tersebut perlu berisi semacam komentar jika tidak ada kode. Praktik umum bagi kosong elseadalah titik koma tunggal ditambah komentar: else { ; // doesn't matter }. Karena tidak ada alasan mengapa ada orang yang hanya mengetikkan titik koma tunggal yang menjorok ke dalam barisnya sendiri. Praktek yang serupa kadang-kadang digunakan dalam loop kosong: while(something) { ; // do nothing }. (kode dengan jeda baris, jelas. komentar SO tidak mengizinkan mereka)
Lundin
5
Saya ingin mencatat, bahwa kode contoh ini dengan dua IF dan yang lain dapat beralih ke yang lain bahkan di perangkat lunak desktop biasa dalam lingkungan multi-utas, jadi nilai mybool dapat diubah di antaranya
Iłya Bursov
61

Perusahaan Anda mengikuti panduan pengkodean MISRA. Ada beberapa versi pedoman ini yang berisi aturan ini, tetapi dari MISRA-C: 2004 :

Aturan 14.10 (wajib): All if… else if konstruksi harus diakhiri dengan klausa else.

Aturan ini berlaku setiap kali pernyataan if diikuti oleh satu atau lebih pernyataan if; final lain ifharus diikuti dengan else pernyataan. Dalam kasus ifpernyataan sederhana maka else pernyataan itu tidak perlu disertakan. Persyaratan untuk else pernyataan akhir adalah pemrograman defensif. The elsepernyataan akan baik mengambil tindakan yang tepat atau berisi komentar yang cocok untuk mengapa tidak ada tindakan yang diambil. Ini sesuai dengan persyaratan untuk memiliki defaultklausa akhir dalam switchpernyataan. Misalnya kode ini adalah pernyataan if sederhana:

if ( x < 0 )
{
 log_error(3);
 x = 0;
} /* else not needed */

sedangkan kode berikut menunjukkan if, else ifkonstruksi

if ( x < 0 )
{
 log_error(3);
 x = 0;
}
else if ( y < 0 )
{
 x = 3;
}
else /* this else clause is required, even if the */
{ /* programmer expects this will never be reached */
 /* no change in value of x */
}

Dalam MISRA-C: 2012 , yang menggantikan versi 2004 dan merupakan rekomendasi saat ini untuk proyek baru, aturan yang sama ada tetapi diberi nomor 15,7 .

Contoh 1: dalam satu pernyataan if programmer mungkin perlu memeriksa n jumlah kondisi dan melakukan operasi tunggal.

if(condition_1 || condition_2 || ... condition_n)
{
   //operation_1
}

Dalam penggunaan biasa melakukan suatu operasi tidak diperlukan setiap saat saat ifdigunakan.

Contoh 2: Di sini pemrogram memeriksa n jumlah kondisi dan melakukan beberapa operasi. Dalam penggunaan biasa if..else ifseperti switchAnda mungkin perlu melakukan operasi seperti default. Jadi penggunaan elsediperlukan sesuai standar misra

if(condition_1 || condition_2 || ... condition_n)
{
   //operation_1
}
else if(condition_1 || condition_2 || ... condition_n)
{
  //operation_2
}
....
else
{
   //default cause
}

Versi saat ini dan yang lalu dari publikasi ini tersedia untuk dibeli melalui toko web MISRA ( melalui ).

Tertanam C
sumber
2
Terima kasih, jawaban Anda adalah semua isi aturan Misra, tapi saya mengharapkan jawaban untuk kebingungan saya di bagian edit pertanyaan.
Trevor
2
Jawaban yang bagus. Karena penasaran, apakah panduan tersebut mengatakan sesuatu tentang apa yang harus dilakukan jika Anda mengharapkan elseklausa tersebut tidak dapat dijangkau? (Tinggalkan kondisi terakhir sebagai gantinya,
buat
17
Saya akan menolak plagiarisme dan tautan yang melanggar hak cipta. Posting saya akan saya edit sehingga menjadi lebih jelas apa kata-kata Anda dan apa kata-kata MISRA. Awalnya jawaban ini hanyalah salinan / tempel mentah.
Lundin
1
@Lundin Saya menerima itu adalah copy / paste dari panduan pengkodean misra. Periksa suntingan pertanyaan dan suntingan jawaban saya itu akan membuat Anda lebih mengerti. Awalnya pertanyaan kosong lalu diedit. Sesuai pertanyaan saya mengedit jawaban saya. Terima kasih atas edit Anda
Tertanam C
9
@TrieuTheVan: Pertanyaan tidak boleh menjadi target bergerak . Pastikan pertanyaan Anda sudah lengkap sebelum Anda mempostingnya.
TJ Crowder
19

Ini sama dengan mewajibkan kasus default di setiap sakelar.

Tambahan lain ini akan Mengurangi cakupan kode program Anda.


Dalam pengalaman saya dengan mem-port kernel linux, atau kode android ke platform yang berbeda sering kali kami melakukan kesalahan dan di logcat kami melihat beberapa kesalahan seperti

if ( x < 0 )
{
    x = 0;
}
else if ( y < 0 )
{
    x = 3;
}
else    /* this else clause is required, even if the */
{       /* programmer expects this will never be reached */
        /* no change in value of x */
        printk(" \n [function or module name]: this should never happen \n");

        /* It is always good to mention function/module name with the 
           logs. If you end up with "this should never happen" message
           and the same message is used in many places in the software
           it will be hard to track/debug.
        */
}
Jeegar Patel
sumber
2
Di sinilah __FILE__dan __LINE__makro berguna untuk membuat lokasi sumber mudah ditemukan jika pesan pernah dicetak.
Peter Cordes
9

Hanya penjelasan singkat, karena saya melakukan ini semua sekitar 5 tahun yang lalu.

Ada (dengan sebagian besar bahasa) tidak ada persyaratan sintaksis untuk menyertakan elsepernyataan "null" (dan tidak perlu {..}), dan dalam "program kecil sederhana" tidak perlu. Tetapi programmer sejati tidak menulis "program kecil yang sederhana", dan, sama pentingnya, mereka tidak menulis program yang akan digunakan sekali dan kemudian dibuang.

Ketika seseorang menulis if / else:

if(something)
  doSomething;
else
  doSomethingElse;

semuanya tampak sederhana dan orang hampir tidak melihat titik penambahan {..}.

Tetapi suatu hari, beberapa bulan dari sekarang, beberapa programmer lain (Anda tidak akan pernah membuat kesalahan seperti itu!) Perlu "meningkatkan" program dan akan menambahkan pernyataan.

if(something)
  doSomething;
else
  doSomethingIForgot;
  doSomethingElse;

Tiba-tiba doSomethingElseagak lupa bahwa seharusnya ada di elsekaki.

Jadi, Anda adalah programmer kecil yang baik dan selalu menggunakannya {..}. Tapi Anda menulis:

if(something) {
  if(anotherThing) {
    doSomething;
  }
}

Semuanya baik-baik saja sampai anak baru itu membuat modifikasi tengah malam:

if(something) {
  if(!notMyThing) {
  if(anotherThing) {
    doSomething;
  }
  else {
    dontDoAnything;  // Because it's not my thing.
  }}
}

Ya, ini tidak diformat dengan benar, tetapi begitu juga setengah kode dalam proyek, dan "pemformat otomatis" akan tercampur oleh semua #ifdefpernyataan. Dan, tentu saja, kode sebenarnya jauh lebih rumit daripada contoh mainan ini.

Sayangnya (atau tidak), saya telah keluar dari hal semacam ini selama beberapa tahun sekarang, jadi saya tidak memiliki contoh "nyata" yang segar dalam pikiran - di atas (jelas) dibuat-buat dan sedikit tipu.

Licks panas
sumber
7

Ini, dilakukan untuk membuat kode lebih mudah dibaca, untuk referensi di kemudian hari dan untuk memperjelas, kepada peninjau kemudian, bahwa kasus yang tersisa ditangani oleh yang terakhir else, adalah kasus tidak melakukan apa-apa , sehingga mereka tidak terlewatkan entah bagaimana pada pandangan pertama.

Ini adalah praktik pemrograman yang baik, yang membuat kode dapat digunakan kembali dan diperluas .

Ahmed Akhtar
sumber
6

Saya ingin menambahkan - dan sebagian bertentangan - jawaban sebelumnya. Meskipun sudah pasti umum untuk menggunakan if-else if dengan cara yang seperti sakelar yang harus mencakup seluruh rentang nilai yang dapat dipikirkan untuk sebuah ekspresi, itu sama sekali tidak dijamin bahwa berbagai kondisi yang mungkin tercakup sepenuhnya. Hal yang sama dapat dikatakan tentang konstruksi sakelar itu sendiri, oleh karena itu persyaratan untuk menggunakan klausa default, yang menangkap semua nilai yang tersisa dan dapat, jika tidak diperlukan, digunakan sebagai pengaman pernyataan.

Pertanyaan itu sendiri menampilkan contoh balasan yang baik: Kondisi kedua tidak berhubungan dengan x sama sekali (itulah alasan mengapa saya sering lebih memilih varian berbasis if yang lebih fleksibel daripada varian berbasis sakelar). Dari contoh tersebut terlihat jelas bahwa jika kondisi A terpenuhi, x harus diset ke nilai tertentu. Jika A tidak terpenuhi, maka kondisi B diuji. Jika terpenuhi, maka x harus menerima nilai lain. Jika A atau B tidak terpenuhi, maka x harus tetap tidak berubah.

Di sini kita dapat melihat bahwa cabang else kosong harus digunakan untuk mengomentari maksud pemrogram untuk pembaca.

Di sisi lain, saya tidak bisa melihat mengapa harus ada klausul lain terutama untuk pernyataan if yang terbaru dan terdalam. Di C, tidak ada yang namanya 'else if'. Hanya ada jika dan lainnya. Sebaliknya, menurut MISRA, konstruksi harus secara formal diindentasi dengan cara ini (dan saya harus meletakkan kurung kurawal buka pada garis mereka sendiri, tetapi saya tidak suka itu):

if (A) {
    // do something
}
else {
    if (B) {
        // do something else (no pun intended)
    }
    else {
        // don't do anything here
    }
}

Ketika MISRA meminta untuk memasang kurung kurawal di setiap cabang, maka hal itu bertentangan dengan dirinya sendiri dengan menyebutkan "jika ... lain jika membangun".

Siapapun bisa membayangkan keburukan pohon yang sangat bersarang, lihat di sini di catatan samping . Sekarang bayangkan bahwa konstruksi ini dapat diperluas secara sewenang-wenang di mana saja. Kemudian meminta klausa lain pada akhirnya, tetapi tidak di tempat lain, menjadi tidak masuk akal.

if (A) {
    if (B) {
        // do something
    }
    // you could to something here
}
else {
    // or here
    if (B) { // or C?
        // do something else (no pun intended)
    }
    else {
        // don't do anything here, if you don't want to
    }
    // what if I wanted to do something here? I need brackets for that.
}

Jadi saya yakin bahwa orang-orang yang mengembangkan pedoman MISRA memiliki keinginan untuk beralih-seperti-jika-jika-niat.

Pada akhirnya, mereka harus menentukan dengan tepat apa yang dimaksud dengan "jika ... lain jika membangun"

Ingo Schalk-Schupp
sumber
5

Alasan dasarnya mungkin adalah cakupan kode dan hal lain yang tersirat: bagaimana kode akan berperilaku jika kondisinya tidak benar? Untuk pengujian yang asli, Anda memerlukan cara untuk melihat bahwa Anda telah diuji dengan kondisi salah. Jika setiap kasus uji yang Anda miliki melalui klausa if, kode Anda dapat mengalami masalah di dunia nyata karena kondisi yang tidak Anda uji.

Namun, beberapa kondisi mungkin benar seperti Contoh 1, seperti pada pengembalian pajak: "Jika hasilnya kurang dari 0, masukkan 0." Anda masih perlu menjalani tes jika kondisinya salah.

TheBick
sumber
5

Secara logis, pengujian apa pun menyiratkan dua cabang. Apa yang Anda lakukan jika itu benar, dan apa yang Anda lakukan jika itu salah.

Untuk kasus di mana salah satu cabang tidak memiliki fungsionalitas, masuk akal untuk menambahkan komentar tentang mengapa tidak perlu memiliki fungsionalitas.

Ini mungkin bermanfaat bagi pemrogram pemeliharaan berikutnya. Mereka tidak perlu mencari terlalu jauh untuk memutuskan apakah kodenya benar. Anda bisa seperti Prehunt the Elephant .

Secara pribadi, ini membantu saya karena memaksa saya untuk melihat kasus lain, dan mengevaluasinya. Ini mungkin merupakan kondisi yang tidak mungkin, dalam hal ini saya dapat mengeluarkan pengecualian karena kontrak dilanggar. Mungkin tidak berbahaya, dalam hal ini komentar sudah cukup.

Jarak tempuh Anda mungkin berbeda.

EvilTeach
sumber
4

Seringkali ketika Anda hanya memiliki satu ifpernyataan, itu mungkin salah satu alasan seperti:

  • Pemeriksaan penjaga fungsi
  • Opsi inisialisasi
  • Cabang pemrosesan opsional

Contoh

void print (char * text)
{
    if (text == null) return; // guard check

    printf(text);
}

Tetapi ketika Anda melakukannya if .. else if, itu mungkin salah satu alasan seperti:

  • Switch-case dinamis
  • Memproses garpu
  • Menangani parameter pemrosesan

Dan jika Anda if .. else ifmencakup semua kemungkinan, dalam hal ini yang terakhir if (...)tidak diperlukan, Anda dapat menghapusnya, karena pada saat itu satu-satunya nilai yang mungkin adalah nilai yang dicakup oleh kondisi itu.

Contoh

int absolute_value (int n)
{
    if (n == 0)
    {
        return 0;
    }
    else if (n > 0)
    {
        return n;
    }
    else /* if (n < 0) */ // redundant check
    {
        return (n * (-1));
    }
}

Dan di sebagian besar alasan ini, mungkin ada sesuatu yang tidak sesuai dengan kategori mana pun di Anda if .. else if, sehingga kebutuhan untuk menanganinya dalam elseklausa terakhir , penanganan dapat dilakukan melalui prosedur tingkat bisnis, pemberitahuan pengguna, mekanisme kesalahan internal, ..etc.

Contoh

#DEFINE SQRT_TWO   1.41421356237309504880
#DEFINE SQRT_THREE 1.73205080756887729352
#DEFINE SQRT_FIVE  2.23606797749978969641

double square_root (int n)
{
         if (n  > 5)   return sqrt((double)n);
    else if (n == 5)   return SQRT_FIVE;
    else if (n == 4)   return 2.0;
    else if (n == 3)   return SQRT_THREE;
    else if (n == 2)   return SQRT_TWO;
    else if (n == 1)   return 1.0;
    else if (n == 0)   return 0.0;
    else               return sqrt(-1); // error handling
}

elseKlausa terakhir ini sangat mirip dengan beberapa hal lain dalam bahasa seperti Javadan C++, seperti:

  • default kasus dalam pernyataan switch
  • catch(...)yang muncul setelah semua catchblok tertentu
  • finally dalam klausa coba-tangkap
Khaled.K
sumber
2

Perangkat lunak kami tidak penting untuk misi, namun kami juga memutuskan untuk menggunakan aturan ini karena pemrograman defensif. Kami menambahkan pengecualian lemparan ke kode yang secara teoritis tidak dapat dijangkau (switch + if-else). Dan itu menyelamatkan kami berkali-kali karena perangkat lunak gagal dengan cepat misalnya ketika tipe baru telah ditambahkan dan kami lupa untuk mengubah satu-atau-dua if-else atau switch. Sebagai bonus, masalah ini menjadi sangat mudah ditemukan.

holdfenytolvaj.dll
sumber
2

Nah, contoh saya melibatkan perilaku yang tidak terdefinisi, tetapi terkadang beberapa orang mencoba menjadi mewah dan gagal, lihat:

int a = 0;
bool b = true;
uint8_t* bPtr = (uint8_t*)&b;
*bPtr = 0xCC;
if(b == true)
{
    a += 3;
}
else if(b == false)
{
    a += 5;
}
else
{
    exit(3);
}

Anda mungkin tidak akan pernah berharap untuk memiliki boolyang bukan trueatau false, bagaimanapun itu mungkin terjadi. Secara pribadi saya percaya ini adalah masalah yang disebabkan oleh orang yang memutuskan untuk melakukan sesuatu yang mewah, tetapi elsepernyataan tambahan dapat mencegah masalah lebih lanjut.

ST3
sumber
1

Saya saat ini bekerja dengan PHP. Membuat formulir pendaftaran dan formulir login. Saya hanya menggunakan if dan else. Tidak ada lagi jika atau apapun yang tidak perlu.

Jika pengguna mengklik tombol kirim -> ini akan masuk ke pernyataan if berikutnya ... jika nama pengguna kurang dari jumlah karakter 'X' maka waspada. Jika berhasil maka periksa panjang kata sandi dan seterusnya.

Tidak perlu kode tambahan seperti yang lain jika itu dapat menghilangkan keandalan waktu buka server untuk memeriksa semua kode tambahan.

Porixify
sumber