İf… else başka bir maddeyle inşa edilirse sonlandırmanın yararı nedir?


136

Kuruluşumuzun aşağıdakileri yapmak için gerekli bir kodlama kuralı vardır (herhangi bir açıklama yapmadan):

if… else eğer yapılar başka bir cümle ile sonlandırılmalıdır

Örnek 1:

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

Örnek 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 */
}

Bu hangi kenar kasayı ele alacak şekilde tasarlanmıştır?

Nedeni hakkında beni de ilgilendiren, Örnek 1'in bir şeye ihtiyaç duymadığı, elseancak Örnek 2'nin ihtiyaç duyduğu . Nedeni yeniden kullanılabilirlik ve genişletilebilirlikse, elseher iki durumda da kullanılması gerektiğini düşünüyorum .


30
Belki de şirketinizden sebep ve fayda isteyin. İlk bakışta, programcıyı düşünmeye ve "eylem gerektirmez" hakkında yorum yapmaya zorlar. Java'nın denetlenen istisnalarının ardındaki aynı mantık (ve en azından tartışmalı).
Thilo

32
Örnek 2 aslında assert(false, "should never go here")mantıklı olabilecek yerlere iyi bir örnektir
Thilo

2
Kuruluşumuz benzer kurallara sahiptir, ancak bu kadar ayrıntılı değildir. Bizim durumumuzdaki amaç iki yönlüdür. İlk olarak, kod tutarlılığı. İkincisi, gevşek dizeler / okunabilirlik yok. İhtiyaç duyulmasa bile başka bir şeye gerek duyulmaması, belgelendirilmemiş olsa bile koda netlik kazandırır. Başka bir gereksinim, geçmişte yaptığım bir şeydir, gerekmediğinde bile, yalnızca uygulamadaki tüm olası sonuçları açıkladığımdan emin olmak için.
LuvnJesus

4
Eğer her zaman yenebilirsin if (x < 0) { x = 0; } else { if (y < 0) { x = 3; }}. Ya da sadece birçoğu aptalca olan bu kurallara uymanız yeterlidir, çünkü bunu yapmanız gerekir.
Jim Balter

3
@Thilo Biraz geç kaldım, ama yine de kimse hatayı yakalamadı: Başka birinin asla gerçekleşmemesi gerektiğine dair bir gösterge yok, sadece yan etkileri olmamalı ( < 0kontroller yaparken normal görünüyor ), bu nedenle iddia devam ediyor programın, muhtemelen değerlerin beklenen sınırlarda olduğu en yaygın durumun ne olduğu konusunda çökmesini sağlamak.
Loduwijk

Yanıtlar:


148

Başka bir cevapta belirtildiği gibi, bu MISRA-C kodlama kılavuzlarından alınmıştır. Amaç, kritik programlamada sıklıkla kullanılan bir kavram olan savunma programlamasıdır.

Yani, her if - else ifbiri bir ile bitmeli elseve her switchbiri bir ile bitmelidir default.

Bunun iki nedeni var:

  • Kendini belgeleyen kod. Eğer bir yazarsanız elseama boş bırakırsanız şu anlama gelir: " Sen ne ifde ne de else ifdoğru olduğunda kesinlikle senaryoyu düşündüm ."

    Bir yazma değil elseorada şu anlama gelir: "Ya ben ne senaryo kabul ifne de else ifdoğrudur, yoksa tamamen dikkate almak unuttum ve şişman böcek benim kodunda burada potansiyel var".

  • Kaçak kodu durdur. Kritik görev yazılımında, pek olası olmayanları bile hesaba katan sağlam programlar yazmanız gerekir. Yani kodu şöyle görebilirsiniz

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

    Bu kod PC programcılarına ve bilgisayar bilimcilerine tamamen yabancı olacak, ancak kritik öneme sahip yazılımlarda mükemmel bir anlam ifade ediyor, çünkü "mybool" un herhangi bir sebepten dolayı bozulduğu durumu yakalar.

    Tarihsel olarak, EMI / gürültü nedeniyle RAM belleğin bozulmasından korkacaksınız. Bu bugün pek bir sorun değil. Daha büyük olasılıkla, kodun başka bir yerinde hatalar nedeniyle bellek bozulması oluşur: yanlış konumlara işaretçiler, sınırların dışına çıkma hataları, yığın taşması, kaçak kod vb.

    Bu nedenle, çoğu zaman, böyle bir kod, uygulama aşamasında hatalar yazdığınızda kendinizi tokatlamak için geri gelir. Yani bir hata ayıklama tekniği olarak da kullanılabilir: yazdığınız program hataları ne zaman yazdığınızı söyler.


DÜZENLE

Neden elseher birinden sonra ihtiyaç duyulmadığı konusunda if:

Bir if-elseveya if-else if-elsetamamen değişkenin sahip olabileceği tüm değerleri kapsar. Ancak if, olası tüm değerleri kapsayacak şekilde açık bir ifade olması gerekmez, çok daha geniş bir kullanımı vardır. Çoğu zaman sadece belirli bir durumu kontrol etmek istersiniz ve karşılanmazsa, hiçbir şey yapmayın. Öyleyse, elsedavayı kapsayacak şekilde savunma amaçlı programlama yazmak anlamlı değildir .

Ayrıca, elseher birinden sonra boş bir yazı yazdıysanız, kodu tamamen karıştıracaktır if.

MISRA-C: 2012 15.7 neden elsegerekli olmadığına dair hiçbir gerekçe sunmuyor , sadece şunu belirtiyor:

Not: elseBasit bir if ifade için son bir ifade gerekli değildir .


6
Bellek bozulursa, belki de kontrol kodunun kendisi de dahil olmak üzere, sadece mybool'dan çok daha fazlasını batırmasını beklerim. Neden if/else if/elsederlediğinizi doğrulayan başka bir blok eklemiyorsunuz ? Ve sonra önceki doğrulayıcıyı doğrulamak için bir tane daha?
Oleg

49
İç çekmek. Bakın arkadaşlar, masaüstü programlamanın ötesinde kesinlikle başka bir deneyiminiz yoksa, açık bir şekilde deneyimlemediğiniz bir şey hakkında her şeyi bilen yorum yapmaya gerek yoktur. Bu her zaman defansif programlama tartışıldığında ve bir PC programcısı durduğunda olur. Bir sebepten dolayı "Bu kod PC programcılarına tamamen yabancı olacak" yorumunu ekledim. Güvenlik açısından kritik öneme sahip yazılımları RAM tabanlı masaüstü bilgisayarlarda çalışacak şekilde programlamazsınız . Dönemi. Kodun kendisi ECC ve / veya CRC sağlama toplamları içeren flash ROM'da bulunur.
Lundin

6
@Deduplicator Gerçekten ( myboolboolean olmayan bir tip olmadığı sürece , C kendi kendine gelmeden önce olduğu gibi bool; derleyici ek statik analiz olmadan varsayımı yapmaz). Ve 'Başka bir şey yazıp boş bırakırsanız' şu anlama gelir: "Kesinlikle ne olursa olsun ne de doğruysa senaryoyu kesinlikle dikkate aldım".: İlk tepkim programcının kod koymayı unuttuğunu varsaymak. başka blok, aksi halde neden boş başka bir blok orada oturuyor? Bir // unusedyorum sadece boş bir blok için uygun değildir.
JAB

5
@JAB Evet, elsekod yoksa bloğun bir tür yorum içermesi gerekir. Boş İçin Ortak uygulama elsetek noktalı virgül artı bir yorumdur: else { ; // doesn't matter }. Kimsenin kendi satırına girintili, tek bir noktalı virgül yazmasının bir nedeni olmadığı için. Benzer uygulama, bazen boş döngüler kullanılır: while(something) { ; // do nothing }. (satır sonları ile kod, açıkçası. SO yorum onlara izin vermiyor)
Lundin

5
İki IF'li bu örnek kodun ve çok iş parçacıklı ortamlardaki olağan masaüstü yazılımlarında bile başka bir yere gidebileceğini belirtmek isterim, bu yüzden
mybool'un

61

Şirketiniz MISRA kodlama rehberliğini izledi. Bu kuralların bu kuralı içeren birkaç sürümü vardır, ancak MISRA-C: 2004 † 'den :

Kural 14.10 (zorunlu): Yapıların başka bir cümleyle sonlandırılması durumunda… if durumunda.

Bu kural, bir if ifadesinin ardından bir veya daha fazla if ifadesi geldiğinde uygulanır; diğer finali ifbir else açıklama izler . Basit bir ififade durumunda , else ifadenin dahil edilmesine gerek yoktur. Son bir else ifade için gereklilik savunma programlamasıdır. elseDeyim ya uygun eylemi veya hiç önlem alınmadığı neden olarak uygun bir yorumunu içerecektir. Bu, defaultbir switchifadede son bir maddeye sahip olma şartı ile tutarlıdır . Örneğin, bu kod basit bir if ifadesidir:

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

Aşağıdaki kod gösterir ise if, else ifyapı

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 */
}

In MISRA-C: 2012 , 2004 versiyonu yerini ve yeni projeler için geçerli öneri, aynı kural var ama 15.7 numaralandırılır .

Örnek 1: Tek bir if deyimi programlayıcının n sayıda koşulu kontrol etmesi gerekebilir ve tek bir işlem gerçekleştirir.

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

Düzenli bir kullanımda, her zaman bir işlem gerçekleştirilmesine gerek yoktur if.

Örnek 2: Burada programcı n sayıda koşulu kontrol eder ve birden fazla işlem gerçekleştirir. Normal kullanımda if..else ifolduğu gibi switch, varsayılan gibi bir işlem yapmanız gerekebilir. Bu nedenle elsemisra standardına göre kullanım gereklidir

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

Bu yayınların güncel ve geçmiş sürümleri MISRA web mağazası ( üzerinden ) aracılığıyla satın alınabilir .


1
Teşekkürler, cevabınız Misra kuralının tüm içeriğidir, ancak sorunun bir kısmını düzenlememdeki karışıklığım için bir cevap bekliyorum.
Trevor

2
İyi cevap. Meraktan, bu rehberler elsemaddede ulaşılamaz olmasını beklerseniz ne yapmanız gerektiği hakkında bir şey söylüyorlar mı? (Bunun yerine son koşulu bırakın, belki bir hata atın?)
jpmc26

17
İntihal ve telif hakkını ihlal eden bağlantılar için oy kullanacağım. Yazıyı, sözlerinizin ve MISRA'nın sözlerinin ne olduğu daha açık hale gelecek şekilde düzenleyeceğim. Başlangıçta bu cevap ham bir kopyala / yapıştırdan başka bir şey değildi.
Lundin

8
@TrieuTheVan: Sorular hareketli hedefler olmamalıdır . Göndermeden önce sorunuzun eksiksiz olduğundan emin olun .
TJ Crowder

1
@ jpmc26 Hayır, ama MISRA'nın amacı size sıkı bir kod vermek değil, size güvenli bir kod vermek. Bugünlerde herhangi bir derleyici ulaşılamaz kodu optimize edecek, bu yüzden dezavantajı yok.
Graham

19

Bu, her anahtarda varsayılan bir durum gerektirmeye eşdeğerdir.

Bu fazladan programınızın kod kapsamını azaltacaktır .


Linux çekirdeğini veya android kodunu farklı bir platforma taşıma deneyimimde birçok kez yanlış bir şey yapıyoruz ve logcat'te bazı hatalar görüyoruz

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.
        */
}

2
Burada __FILE__ve __LINE__makrolar, mesajın yazdırılıp yazdırılmadığını bulmak için kaynak konumunu kolayca bulmak için kullanışlıdır.
Peter Cordes

9

Sadece kısa bir açıklama, çünkü bunu yaklaşık 5 yıl önce yaptım.

"Null" elsedeyimini (ve gereksiz {..}) dahil etmek için (çoğu dilde) sözdizimsel bir gereklilik yoktur ve "basit küçük programlarda" gerek yoktur. Ancak gerçek programcılar "basit küçük programlar" yazmazlar ve en önemlisi, bir kez kullanılacak ve sonra atılacak programları yazmazlar.

Bir if / else yazdığında:

if(something)
  doSomething;
else
  doSomethingElse;

her şey basit görünüyor ve kişi ekleme noktasını bile zor görüyor {..}.

Ama bir gün, birkaç ay sonra, başka bir programcı (asla böyle bir hata yapmazsınız!) Programı "geliştirmek" ve bir açıklama eklemek gerekir.

if(something)
  doSomething;
else
  doSomethingIForgot;
  doSomethingElse;

Birdenbire bacağında doSomethingElseolması gerektiğini unutur else.

Yani iyi bir programcısın ve her zaman kullanıyorsun {..}. Ama siz yazıyorsunuz:

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

Yeni çocuk gece yarısı modifikasyonu yapana kadar her şey yolunda ve iyi:

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

Evet, yanlış biçimlendirilmiş, ancak projedeki kodun yarısı da öyle ve "otomatik biçimlendirici" tüm #ifdefifadelerle bollixed oluyor . Ve elbette, gerçek kod bu oyuncak örneğinden çok daha karmaşık.

Ne yazık ki (ya da değil), birkaç yıldır bu tür bir şeyden uzaktayım, bu yüzden aklımda yeni bir "gerçek" örneğim yok - yukarıdakiler (açıkçası) yapmacık ve biraz hokey.


7

Bu, daha sonraki referanslar için, kod daha okunabilir hale getirmek ve son tarafından ele kalan vakalar olduğunu, daha sonraki inceleme için, temizlemek yapmak için yapılır elsevardır bir şey yapmak onlar ilk bakışta bir şekilde gözden değil böylece, davaları.

Bu, kodu yeniden kullanılabilir ve genişletilebilir hale getiren iyi bir programlama uygulamasıdır .


6

Önceki cevaplara - ve kısmen çelişki- eklemek istiyorum. İfadenin tüm düşünülebilir değerlerini kapsaması gereken anahtar benzeri bir şekilde if-else kullanılması kesinlikle yaygın olsa da, hiçbir şekilde olası koşulların tamamen karşılandığı garanti edilemez. Aynı şey, anahtar yapısının kendisi için de söylenebilir, bu nedenle, kalan tüm değerleri yakalayan ve aksi halde gerekli değilse, bir iddia koruması olarak kullanılabilen varsayılan bir cümle kullanma gereksinimi.

Sorunun kendisi iyi bir karşı örneğe sahiptir: İkinci koşul hiç x ile ilgili değildir (bu yüzden anahtar tabanlı varyant yerine daha esnek if tabanlı varyantı tercih etmemin nedeni budur). Örnekte, A koşulu karşılandığında, x'in belirli bir değere ayarlanması gerektiği açıktır. A karşılanmazsa, koşul B test edilir. Karşılanırsa, x başka bir değer almalıdır. Ne A ne de B karşılanmazsa, x değişmeden kalmalıdır.

Burada, programcının okuyucuya niyeti hakkında yorum yapmak için başka bir boş dalın kullanılması gerektiğini görebiliriz.

Öte yandan, özellikle son ve en içteki if ifadesi için neden başka bir madde olması gerektiğini göremiyorum. C'de, 'başka bir şey' diye bir şey yoktur. Sadece ve varsa vardır. Bunun yerine, MISRA'ya göre, yapı bu şekilde resmen girintili olmalıdır (ve açılış kıvırcık parantezleri kendi hatlarına koymalıydım, ama bunu sevmiyorum):

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

MISRA her dalın etrafına kıvırcık parantez koymayı istediğinde, "if ... else ifşalar" dan bahsederek kendisiyle çelişir.

Herkes derinden yuvalanmış çirkinliği başka ağaçlar varsa hayal edebilir, burada bir yan notta bakın . Şimdi bu yapının keyfi olarak herhangi bir yere genişletilebileceğini düşünün. Sonra sonunda başka bir madde istemek, ama başka hiçbir yerde istememek saçma olur.

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

Bu yüzden eminim ki, MISRA yönergelerini geliştiren insanlar, eğer akıllarında ise, eğer başka bir şeye benziyorlardı.

Sonunda, bir "if ... else if construct" ile ne kastedildiğini tam olarak tanımlamaları onlar için


5

Temel neden muhtemelen kod kapsamı ve diğer örtük: koşul doğru değilse kod nasıl davranacak? Gerçek testler için false koşulu ile test yaptığınızı görmek için bir yol bulmanız gerekir. Sahip olduğunuz her test senaryosu if deyiminden geçiyorsa, test etmediğiniz bir koşul nedeniyle kodunuzun gerçek dünyada sorunları olabilir.

Ancak, vergi beyanında olduğu gibi bazı koşullar Örnek 1'deki gibi olabilir: "Sonuç 0'dan küçükse 0 girin." Hala durumun yanlış olduğu bir teste ihtiyacınız vardır.


5

Mantıksal olarak herhangi bir test iki dalı ima eder. Doğru ise ne yaparsınız, yanlışsa ne yaparsınız.

Her iki dalın da işlevselliği olmadığı durumlarda, neden işlevselliğe ihtiyaç duymadığı hakkında bir yorum eklemek mantıklıdır.

Bu, bir sonraki bakım programcısı için faydalı olabilir. Kodun doğru olup olmadığına karar vermek için çok fazla arama yapmaları gerekmez. Bir çeşit Fil Öncesi olabilir .

Şahsen, beni başka bir duruma bakmaya ve değerlendirmeye zorladığı için bana yardımcı oluyor. İmkansız bir durum olabilir, bu durumda sözleşme ihlal edildiğinden bir istisna atabilirim. İyi huylu olabilir, bu durumda bir yorum yeterli olabilir.

Kilometreniz değişebilir.


4

Çoğu zaman tek bir ififadeniz olduğunda, muhtemelen aşağıdakilerden biri olabilir:

  • Fonksiyon koruma kontrolleri
  • Başlatma seçeneği
  • İsteğe bağlı işleme dalı

Misal

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

    printf(text);
}

Ancak bunu yaptığınızda if .. else if, muhtemelen aşağıdakilerden biri olabilir:

  • Dinamik anahtar kutusu
  • İşleme çatalı
  • Bir işleme parametresini işleme

Ve if .. else iftüm olasılıklarınızı if (...)kapsaması durumunda, bu durumda sonuncunuza ihtiyaç duyulmaz, sadece kaldırabilirsiniz, çünkü bu noktada sadece olası değerler bu koşulun kapsadığı değerlerdir.

Misal

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

Ve bu nedenlerin çoğunda, bir şeylerin kategorilerinizle uyuşmaması olasıdır if .. else if, bu nedenle bunları son bir elsemaddede ele alma ihtiyacı, iş seviyesi prosedürü, kullanıcı bildirimi, dahili hata mekanizması, ..vb.

Misal

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

Bu son elsemadde, Javave gibi dillerdeki diğer birkaç şeye oldukça benzer C++:

  • default bir switch deyiminde durum
  • catch(...)tüm özel catchbloklardan sonra gelen
  • finally bir try-catch maddesinde

2

Yazılımımız görev açısından kritik değildi, ancak savunma kurallarından dolayı bu kuralı kullanmaya karar verdik. Teorik olarak erişilemeyen koda bir atma istisnası ekledik (başka bir deyişle switch +). Yazılım, örneğin yeni bir tür eklendiğinde hızlı bir şekilde başarısız olduğu için bizi birçok kez kurtardı ve eğer bir-iki veya başka bir anahtar veya anahtarı değiştirmeyi unuttuk. Bir bonus olarak sorunu bulmak çok kolay.


2

Örneğim, tanımsız davranışlar içeriyor, ancak bazen bazı insanlar süslü olmaya çalışıyor ve çok başarısız oluyor, bir göz atın:

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

Muhtemelen olması asla istemez boolalmayan truene de false, ancak ortaya çıkabilir. Şahsen bunun fantezi bir şey yapmaya karar veren kişinin neden olduğu sorun olduğuna inanıyorum, ancak ek elseaçıklama başka sorunları önleyebilir.


1

Şu anda PHP ile çalışıyorum. Bir kayıt formu ve bir giriş formu oluşturma. Sadece if ve else kullanıyorum. Gerekirse ya da gereksiz bir şey yok.

Kullanıcı gönder düğmesini tıklarsa -> bir sonraki if ifadesine gider ... kullanıcı adı 'X' karakter sayısından azsa uyarır. Başarılı olursa şifre uzunluğunu kontrol edin vb.

Tüm ekstra kodları kontrol etmek için sunucu yükleme süresi güvenilirliğini ortadan kaldırabiliyorsa, başka bir koda gerek yoktur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.