Bir C her küçük hata için kontrol etmeli mi?


60

İyi bir programcı olarak, programın her sonucuna cevap verecek sağlam kodlar yazılmalıdır. Ancak, C kütüphanesindeki hemen hemen tüm fonksiyonlar bir hata olduğunda 0 veya -1 veya NULL döndürür.

Örneğin, bir dosyayı açmaya çalıştığınızda hata kontrolünün gerekli olduğu açıktır. Ama sık sık hata gibi fonksiyonları kontrol görmezden printfhatta mallocben gerekli hissetmiyorum çünkü.

if(fprintf(stderr, "%s", errMsg) < 0){
    perror("An error occurred while displaying the previous error.");
    exit(1);
}

Sadece belirli hataları yoksaymak iyi bir uygulama mıdır yoksa tüm hataları ele almanın daha iyi bir yolu var mı?


13
Üzerinde çalıştığınız proje için gerekli olan sağlamlık seviyesine bağlıdır. Güvenilmez taraflardan (örneğin halka açık sunuculardan) girdi alma veya tam olarak güvenilmeyen ortamlarda çalışma şansına sahip olan sistemler, kodun geçme saatli bir bomba (veya en zayıf bağlantının kesilmesinin önlenmesini) önlemek için çok dikkatli kodlanmalıdır. ). Açıkçası, hobi ve öğrenme projeleri bu kadar sağlamlığa ihtiyaç duymuyor.
rwong

1
Bazı diller istisnalar sağlar. İstisnaları yakalamazsanız, iş parçacığınız sona erecek ve bu kötü durumda devam etmesine izin vermekten daha iyidir. İstisnaları yakalamayı seçerseniz, çağrılan işlevler ve bir tryifade içeren yöntemler de dahil olmak üzere birçok kod satırındaki birçok hatayı kapatabilirsiniz , böylece her bir çağrıyı veya işlemi kontrol etmeniz gerekmez. (Ayrıca bazı diller aut boş dereference veya dizi indeksi gibi basit hataları algılama konusunda diğerlerinden daha iyi olduğunu unutmayın.)
Erik eidt

1
Sorun çoğunlukla metodolojik biridir: Eğer hala uygulamak gerekiyor ne bulmaktan zaman tipik hata denetimi yazmayın ve yok istiyorum siz "mutlu yol" almak istiyorum çünkü şu anda kontrol hatayı eklemek doğru ilk. Ama yine de malloc ve co'yu kontrol etmen gerekiyor . Hata kontrolü adımını atlamak yerine, kodlama faaliyetlerinizi önceliklendirmeniz ve hata kontrolünün TODO listenizde kodunuzdan memnun olduğunuzda uygulanan kalıcı bir yeniden düzenleme adımı olmasını sağlamanız gerekir. Greppable "/ * CHECK * /" yorumu eklemek yardımcı olabilir.
coredump,

7
Kimsenin bahsetmediğine inanamıyorum errno! Aşina olmadığınız durumlarda, “C kütüphanesindeki hemen hemen tüm işlevler 0 veya −1 döndürür veya NULLbir hata olduğunda” olduğu doğru olsa daerrno , kullanarak #include <errno.h>ve sonra okuyarak erişebileceğiniz genel değişkeni de belirlerler. değeri errno. Bu nedenle, örneğin, open(2) dönerse -1, errno == EACCESbunun hangi izin hatasını göstereceğini veya ENOENThangi dosyanın mevcut olmadığını gösterip göstermeyeceğini kontrol etmek isteyebilirsiniz .
wchargin

1
@ErikEidt C desteklemediği try/ catchatlar ile simüle rağmen.
Mast

Yanıtlar:


29

Genel olarak, kod uygun olan her yerde istisnai koşullarla ilgilenmelidir. Evet, bu belirsiz bir ifadedir.

Yazılım istisnası kullanımına sahip üst düzey dillerde bu genellikle "bununla ilgili bir şeyler yapabileceğiniz yöntemde istisnayı yakalamak" olarak belirtilir. Bir dosya hatası meydana geldiyse, belki de kullanıcıya "Dosyanıza diske kaydedemedi" diyen yığını UI koduna çevirmesine izin verdiniz. İstisna mekanizması "her küçük hatayı" etkin bir şekilde yutar ve örtülü olarak uygun bir yerde tutar.

C de bu lüksün yok. Bazıları kodlama uygulamaları olan bazıları dil / kütüphane özellikleri olan hatalarla başa çıkmanın birkaç yolu vardır.

Sadece belirli hataları yoksaymak iyi bir uygulama mıdır yoksa tüm hataları ele almanın daha iyi bir yolu var mı?

Bazı hataları yoksay? Olabilir. Örneğin, standart çıktıya yazmanın başarısız olmayacağını varsaymak mantıklıdır. Başarısız olursa, yine de kullanıcıya nasıl söylersiniz? Evet, belirli hataları yoksaymak veya bunları önlemek için savunmak için savunmak iyi bir fikirdir. Örneğin, bölmeden önce sıfırı kontrol edin.

Hataların tümünü veya en azından hatalarını ele almanın yolları vardır:

  1. Hata yönetimi için, atlayışa benzer atlamaları kullanabilirsiniz . Yazılım uzmanları arasında tartışmalı bir konu olsa da, özellikle gömülü ve performans açısından kritik kodda (örneğin, Linux çekirdeği) bunlar için geçerli kullanımlar vardır.
  1. Basamaklı ifs:

    if (!<something>) {
      printf("oh no 1!");
      return;
    }
    if (!<something else>) {
      printf("oh no 2!");
      return;
    }
    
  2. İlk durumu test edin, örneğin bir dosyayı açma veya oluşturma, ardından sonraki işlemlerin başarılı olduğunu varsayalım.

Sağlam kod iyidir ve bir hata olup olmadığını kontrol etmeli ve ele almalıdır. Kodunuz için hangi yöntemin en iyi olduğu kodun ne yaptığına, bir hatanın ne kadar kritik olduğuna vb. Bağlıdır ve yalnızca bunu gerçekten cevaplayabilirsiniz. Bununla birlikte, bu yöntemler savaşta test edilir ve gerçek kodun hataları nasıl kontrol ettiğini görmek için göz atabileceğiniz çeşitli açık kaynaklı projelerde kullanılır.


21
Maalesef burada seçeceğiniz az miktarda nit - "standart çıktıya yazma başarısız olmayacak. Eğer başarısız olursa, nasıl olsa kullanıcıya söylersiniz?" - standart hataya yazarak? Birine yazmadaki başarısızlığın diğerine yazmanın imkansız olduğu anlamına gelmez.
Damien_The_Unbeliever 17:15

4
@Damien_The_Unbeliever - özellikle stdoutbir dosyaya yönlendirilebildiğinden veya sisteme bağlı olarak bile bulunmadığından. stdoutBir "konsola yaz" akışı değil, genellikle böyledir, ancak olması gerekmez.
Davor Ždralo 17:15

3
@JAB Bir mesaj yolla stdout... açık :-)
TripeHound 17:15

7
@JAB: Eğer uygunsa, EX_IOERR ile çıkabilirsiniz.
John Marshall

6
Ne yaparsan yap, yok sadece hiçbirşey olmamış gibi çalışan devam! Komut dump_database > backups/db_dump.txtherhangi bir noktada standart çıktıya yazamazsa, devam etmesini ve başarılı bir şekilde çıkmasını istemem. (Veritabanları bu şekilde yedeklenmez, ancak nokta hala durur)
Ben S

15

Ancak, C kütüphanesindeki hemen hemen tüm fonksiyonlar bir hata olduğunda 0 veya -1 veya NULL döndürür.

Evet, ama hangi işlevi çağırdığınızı biliyorsunuz , değil mi?

Aslında bir hata iletisine yerleştirebileceğiniz birçok bilginiz var. Hangi işlevin çağrıldığını, onu arayan işlevin adını, hangi parametrelerin geçtiğini ve işlevin döndürdüğünü biliyorsunuz. Bu çok bilgilendirici bir hata mesajı için bol miktarda bilgi.

Her işlev çağrısı için bunu yapmanız gerekmez. Ancak, "önceki hatayı görüntülerken bir hata oluştu" hata mesajını ilk gördüğünüzde, gerçekten ihtiyaç duyduğunuz şey faydalı bilgiler olduğunda, bu hata mesajını en son gördüğünüz zaman olacaktır, çünkü hemen değiştireceksiniz. Hata mesajı, sorunu gidermenize yardımcı olacak bir bilgi verici mesajdır.


5
Bu cevap beni güldürdü, çünkü doğru, ama soruyu cevaplamıyor.
RubberDuck 17:15

Soruyu nasıl cevaplamıyor?
Robert Harvey

2
Bunun bir nedeni, C'nin (ve diğer dillerin) boolean 1 ve 0'ı karıştırmasının bir hata kodu döndüren düzgün bir işlevin hatasız olarak "0" döndürmesiyle aynı sebepten kaynaklandığını düşünüyorum. ama sonra söylemelisin if (!myfunc()) {/*proceed*/}. 0 hata yapmadı ve sıfır olmayan herhangi bir şey bir tür hatamdı ve her bir hatanın kendi sıfır olmayan kodu vardı. bir arkadaşımın ifade ettiği gibi "sadece bir Gerçek var, ama birçok yanlışlık var". if()Testte "0" "doğru" olmalı ve sıfır olmayan herhangi bir şey "yanlış" olmalıdır.
robert bristow-johnson, 17:15

1
hayır, sizin tarzınıza göre, olası bir olumsuz hata kodu var. ve birçok olası no_error kodu.
robert bristow-johnson, 17:15

1
@ robertbristow-johnson: Sonra "Hata yoktu" olarak okuyun. if(function()) { // do something }"eğer fonksiyon hatasız çalışıyorsa, o zaman bir şeyler yap" yazıyor. veya daha da iyisi, "işlev başarılı bir şekilde yürütülürse, bir şey yapın." Cidden, sözleşmeyi anlayanlar bununla karıştırılmaz. falseBir hata oluştuğunda geri dönmek (veya 0) hata kodlarının kullanılmasına izin vermez. Sıfır C de yanlış demektir, çünkü matematik böyle çalışır. Bkz programmers.stackexchange.com/q/198284
Robert Harvey

11

TLDR; neredeyse hataları asla görmezden gelmemelisin.

C dili, her kütüphane geliştiricisinin kendi çözümlerini gerçekleştirmesi için iyi bir hata işleme özelliğinden yoksundur. Daha modern dillerin, bu özel sorunu ele almayı çok daha kolay hale getiren istisnaları vardır.

Fakat C ile sıkışıp kaldığınızda böyle bir avantaja sahip değilsinizdir. Ne yazık ki, uzak bir başarısızlık olasılığı olan bir işlevi çağırdığınızda, fiyatı yalnızca ödemek zorunda kalacaksınız. Yoksa, istemeden bellekteki verilerin üzerine yazma gibi daha kötü sonuçlara maruz kalırsınız. Genel bir kural olarak, hataları her zaman kontrol etmeniz gerekir.

Geri dönüşünü kontrol etmiyorsanız, bunun fprintfarkasında bir hata bırakmanız muhtemeldir, en iyi durumda, kullanıcının beklediği şeyi yapmaz ve daha kötüsü uçuş sırasında her şeyi patlatır. Kendine bu şekilde zarar vermek için hiçbir sebep yok.

Bununla birlikte, bir C geliştiricisi olarak, kodu korumak için kolaylık sağlamak aynı zamanda sizin görevinizdir. Bu nedenle, bazen uygulamanın genel davranışları için herhangi bir tehdit oluşturmazlarsa (ve sadece eğer) netlik açısından hataları göz ardı edebilirsiniz . .

Bunu yapmakla aynı problem:

try
{
    run();
} catch (Exception) {
    // comments expected here!!!
}

Boş catch bloğunun içinde iyi bir yorum bulunmadığını görürseniz, bu kesinlikle bir konudur. Bir aramanın malloc()zamanın% 100'ünü başarıyla gerçekleştireceğini düşünmek için hiçbir neden yoktur .


Sürdürülebilirliğin gerçekten önemli bir yönü olduğuna katılıyorum ve bu paragraf sorumu cevapladı. Detaylı cevap için teşekkürler.
Derek,

Malloc aramalarını kontrol etmekte hiç sıkıntı çekmeyen ve bir daha asla çökmeyen programların soluksuzluğunun, bir masaüstü programı yalnızca birkaç MB bellek kullandığında tembel olmak için iyi bir neden olduğunu düşünüyorum.
whatsisname,

5
@whatsisname: Sadece çökmemeleri, verileri sessizce bozmayacakları anlamına gelmez. dönüş değerlerini kesinlikle kontrol etmek istediğiniz malloc()bir fonksiyondur !
TMN

1
@TMN: Eğer malloc başarısız olursa, program derhal kırılır ve sonlandırılır, bir şeyler yapmaya devam etmezdi. Her şey risk ve neyin uygun olduğu, "neredeyse asla" değil.
whatsisname,

2
@Alex C, iyi bir hata işlemeden yoksundur. İstisnalar istiyorsanız, onları kullanabilirsiniz. İstisnaları kullanmayan standart kütüphane kasıtlı bir seçimdir (istisnalar sizi otomatik hafıza yönetimine zorlar ve genellikle düşük seviye kod için istemezsiniz).
martinkunev 17:15

9

Soru aslında dile özgü değil, kullanıcıya özeldir. Soruyu kullanıcı bakış açısından düşünün. Kullanıcı programın ismini komut satırına yazmak ve enter tuşuna basmak gibi bir şey yapar. Kullanıcı ne bekliyor? Bir şeylerin yanlış gittiğini nasıl söyleyebilirler? Bir hata oluşursa araya girmeyi göze alabilir mi?

Pek çok kod türünde, bu tür kontroller aşırı yüklenmez. Bununla birlikte, yüksek güvenilirlikli güvenlik nükleer reaktörleri için olduğu gibi kritik öneme sahip kodlarda, patolojik hata kontrolü ve planlı iyileşme yolları işin günlük yapısının bir parçasıdır. "X başarısız olursa ne olur? Güvenli bir duruma nasıl dönerim?" Video oyunları için olduğu gibi daha az güvenilir kodda, çok daha az hata kontrolüyle kurtulabilirsiniz.

Benzer bir yaklaşım ise hatayı yakalayarak durumu ne kadar geliştirebildiğinizdir. İstisnaları gururla karşılayan C ++ programlarının sayısını sayamıyorum, sadece onları yeniden başlatmak için, çünkü aslında onlarla ne yapacaklarını bilmiyorlardı ... ama istisna yönetimi yapmaları gerektiğini biliyorlardı. Bu tür programlar ekstra çabadan hiçbir şey kazanmamıştır. Yalnızca, aslında hata kodunu kontrol etmekten çok, durumu daha iyi idare edebileceğini düşündüğünüz hata kontrol kodu ekleyin. Bununla birlikte, C ++ 'ın daha anlamlı bir şekilde yakalamak için istisnaların kullanımı sırasında ortaya çıkan istisnaları ele alma konusunda özel kuralları vardır (ve bununla, kendiniz için kurduğunuz cenazenin aydınlandığından emin olmak için terminate ()' i çağırmak anlamına gelir. uygun görkemiyle)

Ne kadar patolojik alabilirsiniz? Dikkatli bir şekilde 1 ve 0 dağılımını sağlamak için seçilmiş doğru ve yanlış değerleri olan bir "SafetyBool" tanımlayan bir program üzerinde çalıştım ve bunlar bir dizi donanım arızasından herhangi birinin (tek bit, veri yolu, herhangi bir hata vereceği şekilde) seçildi. izlerin kırılması vs.) bir boolenin yanlış yorumlanmasına neden olmadı. Söylemeye gerek yok, bunun eski herhangi bir programda kullanılacak genel amaçlı bir programlama uygulaması olduğunu iddia etmem.


6
  • Farklı güvenlik gereksinimleri farklı seviyelerde doğruluk gerektirir. Havacılık veya otomobil kontrol yazılımında, tüm iade değerleri kontrol edilecektir, cf. MISRA.FUNC.UNUSEDRET . Makinenizi asla terk etmeyen konseptin hızlı bir kanıtı, muhtemelen değil.
  • Kodlama zaman alır. Güvenli olmayan kritik yazılımlarda çok fazla alakasız kontrol başka bir yerde daha iyi harcanan çabadır. Peki kalite ve maliyet arasındaki tatlı nokta nerede? Bu hata ayıklama araçlarına ve yazılımın karmaşıklığına bağlıdır.
  • Hata işleme, kontrol akışını engelleyebilir ve yeni hatalar ortaya çıkarabilir. En azından hataları bildiren Richard "network" Stevens'ın sarmalayıcı işlevlerini çok seviyorum.
  • Hata işleme, nadiren bir performans sorunu olabilir. Ancak çoğu C kütüphanesi çağrısı o kadar uzun sürecektir ki, bir dönüş değerini kontrol etmenin maliyeti ölçülemez derecede düşüktür.

3

Biraz soru soyut. Ve mutlaka C dili için değil.

Daha büyük programlar için bir soyutlama katmanına sahip olursunuz; belki motorun bir parçası, bir kütüphane veya bir çerçeve içinde. Bu katman, geçerli bir veri elde hava durumu hakkında umurumda olmaz veya çıkış bazı varsayılan değer olacaktır: 0, -1, nullvb

Sonra, soyut katmana sizin arayüzünüz olacak, çok fazla hata yapma ve belki de bağımlılık enjeksiyonları, olay dinleme vb.

Ve daha sonra, kuralları koyduğunuz ve çıktıları yönettiğiniz somut uygulama katmanınıza sahip olacaksınız.

Bu yüzden benim üstesinden geldiğimde, bazen hata işlemeyi kodun bir kısmından tamamen hariç tutmanın daha iyi olacağıdır çünkü bu kısım bu işi yapmaz. Ve sonra çıkışı değerlendiren ve bir hataya işaret eden bir işlemciye sahip olun.

Bu daha çok kod okunabilirliği ve daha iyi ölçeklenebilirliğe yol açan sorumlulukları ayırmak için yapılır.


0

Genel olarak, bu hata durumunu kontrol etmemek için iyi bir nedeniniz yoksa , yapmanız gerekir. Bir hata durumunu kontrol etmemeyi düşünebilmemin tek iyi nedeni, başarısız olması durumunda anlamlı bir şey yapamamanızdır. Bu olsa bile buluşması çok zor bir bar çünkü her zaman uygun bir seçenek var: zarif bir şekilde çıkın.


Genelde size katılmıyorum çünkü hatalar hesaba katmadığınız durumlar. Hatanın hangi koşulda yayıldığını bilmiyorsanız hatanın nasıl ortaya çıkacağını bilmek zordur. Ve böylece verilerin fiili işlenmesinden önce hata işleme.
Creative Magic

Mesela bir şey dönerse ya da bir hata atarsa, hatayı nasıl düzeltip devam edeceğinizi bilmeseniz bile, her zaman zarafetle başarısız olabilir , hatayı günlüğe kaydedebilir, kullanıcıya beklendiği gibi çalışmadığını söyleyebilirsiniz, vb. Önemli olan nokta, hatanın fark edilmemesi, silinmemesi, "sessizce başarısız olması" veya uygulamada çökmemesi gerektiğidir. "Nazikçe başarısız" veya "nazikçe çık" demek.
Zeki Neologizm
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.