C kütüphanesi fonksiyonlarının arkasındaki mantık errno'yu asla sıfıra ayarlamaz


9

C standardı hiçbir C standart kütüphane fonksiyonunun errnosıfıra ayarlanmamasını zorunlu kılar . Neden tam olarak bu?

Birkaç işlevi çağırmak ve sadece errnosonuncusundan sonra kontrol etmek için yararlı olduğunu anlayabiliyordum - örneğin:

errno = 0;
double x = strtod(str1, NULL);
long y = strtol(str2, NULL);
if (errno)
    // either "strtod" or "strtol" failed
else
    // both succeeded

Ancak, bu "kötü uygulama" olarak değerlendirilmiyor mu? Yalnızca kontrol ediyoruz yana errnoen sonunda, sadece işlevlerinden biri olduğunu biliyoruz yaptı başarısız değil, hangi başarısız işliyor. Bir şeyin çoğu pratik program için yeterince başarılı olmadığını bilmek mi?

Çeşitli C Rationale belgeleri aramaya çalıştım, ancak birçoğunun fazla ayrıntısı yok <errno.h>.


1
Sanırım "istemediğin şey için ödeme yapma". Eğer umursuyorsanız errno, onu her zaman sıfıra ayarlayabilirsiniz.
Kerrek SB

2
Dikkat edilmesi gereken başka bir şey, bir fonksiyonun errnobaşarılı olsa bile sıfırdan farklı bir değere ayarlanabilmesidir . (Başarısız olan başka bir işlev çağırabilir, ancak bu dış işlevde bir başarısızlık oluşturmaz.)
Keith Thompson

Yanıtlar:


10

C kütüphanesi errnotarihsel nedenlerden dolayı 0 olarak ayarlanmamıştır 1 . POSIX, artık kitaplıklarının başarı durumunda değeri değiştirmeyeceğini iddia etmiyor ve yeni Linux kılavuz sayfasıerrno.h bunu yansıtıyor:

<errno.h>Başlık dosyası tamsayı değişkeni tanımlayan errnosistem çağrıları ve neyin yanlış gittiğini belirtmek için bir hata durumunda bazı kütüphane fonksiyonları tarafından belirlenir. Değeri yalnızca çağrının dönüş değeri bir hata gösterdiğinde (yani, -1çoğu sistem çağrısından -1veya NULLçoğu kütüphane fonksiyonundan) önemlidir ; başarılı olan bir fonksiyonun değişmesine izin verilir errno.

ANSI C Gerekçe komitesi benimsenmesinin ve kullanmanın mevcut uygulamaları standart hale daha pratik olduğunu hissettim belirtiyor errno.

Ayarı üzerinde odaklanan hata raporlama makineleri errnogenellikle en iyi toleransla değerlendirilir. Kütüphane işlevleri arasında `` patolojik bir bağlantı '' gerektirir ve paylaşılabilir kütüphanelerin yapımına müdahale eden statik yazılabilir bellek hücresini kullanır. Bununla birlikte, Komite daha iddialı bir şey icat etmek yerine, bu eksik, ancak mevcut olan makineleri standartlaştırmayı tercih etmiştir.

Hemen hemen her zaman errnoayarlanmış olup olmadığını kontrol dışında hata kontrol etmek için bir yolu vardır . Olmadığını kontrol etme errnovar kümesi her zaman güvenilir değildir bazı aramalar hata nedeni almak için ayrı bir API çağrısı gerektirir beri. Örneğin ferror(), fread()veya öğesinden kısa bir sonuç alırsanız bir hatayı kontrol etmek için kullanılır fwrite().

İlginçtir ki, kullanmanın senin örneğin strtod()ayarlarken durumlarda biridir errnoçağrı önce 0'a gerekli bir hata oluştu eğer doğru algılamak için. Tüm strto*()dize-sayı işlevleri bu gereksinime sahiptir, çünkü bir hata karşısında bile geçerli bir dönüş değeri döndürülür.

errno = 0;
char *endptr;
double x = strtod(str1, &endptr);
if (endptr == str1) {
    /*...parse error */
} else if (errno == ERANGE) {
    if (x == 0) {
        /*...underflow */
    } else if (x == HUGE_VAL) {
        /*...positive overflow */
    } else if (x == -HUGE_VAL) {
        /*...negative overflow */
    } else {
        /*...unknown range error? */
    }
}

Yukarıdaki kod, strtod()Linux'ta belgelendiği şekliyle davranışı temel alır . C standardı, yalnızca düşük akışın en küçük pozitif değerden daha büyük bir değer döndüremeyeceğini doubleve uygulama errnoolarak ayarlanıp ayarlanmayacağını ERANGEuygulama 2 olarak tanımlamaktadır .

Aslında bir kütüphane çağrısından önce daima 0 olarak ayarlanmasını ve çağrıdan sonra değerinin kontrol edilmesini öneren kapsamlı bir sertifika danışma yazımı vardır . Bunun nedeni , çağrının kendisi başarılı olsa bile bazı kütüphane çağrılarının ayarlanmasıdır 3 .errnoerrno

errnoProgram başlangıcında 0 değeri , ancak hiçbir kütüphane işlevi tarafından asla 0 olarak ayarlanmaz. Kullanımı , C Standardındaki işlevin açıklamasında belgelenmediği errnosürece, bir hata olup olmadığına bakılmaksızın kütüphane işlev çağrısı tarafından sıfırdan farklı olarak ayarlanabilir errno. Bir programın içeriğini errnoancak bir hata bildirildikten sonra incelemesi anlamlıdır . Daha doğrusu, errnosadece errnohataya ayarlanan bir kütüphane fonksiyonu bir hata kodu döndürdükten sonra anlamlıdır .


1. Daha önce, önceki bir çağrıdan gelen bir hatayı maskelemekten kaçınmak olduğunu iddia ettim. Bu iddiayı destekleyecek hiçbir kanıt bulamıyorum. Ayrıca sahte bir printf()örnek vardı .
2. @chux'a bunu işaret ettiği için teşekkürler. Referans C.11 §7.22.1.3 ¶10'dur.
3. @KeithThompson tarafından bir yorumda belirtildi.


Küçük sorun: düşük akış 0 olmayan bir sonuçla sonuçlanabilir: "işlevler, dönüş tipindeki büyüklüğü normalleştirilmiş en küçük pozitif sayıdan daha büyük olmayan bir değer döndürür" C11 7.22.1.3.10.
chux - Monica'yı geri yükle

@chux: Teşekkürler. Bir düzenleme yapacağım. Ayarı yana errnoetmek ERANGEYetersizlik durumunda uygulanmasını tanımlanır, hiçbir aslında Yetersizlik algılamak için taşınabilir bir yolu yoktur. Kodum, sistemimdeki Linux kılavuz sayfasında bulduğum şeye uyuyordu.
jxh

strto*İşlevler hakkındaki yorumlarınız tam olarak neden örneğimin kötü uygulama olarak değerlendirilip değerlendirilmeyeceğini sorduğumdur, ancak errnosıfıra ayarlanmamanın yararlı olması veya hatta uygulanması için tek yol budur .

@DrewMcGowen: Sanırım alabileceğiniz tek nokta errno, başarı durumunda bile ayarlanabileceğidir, bu yüzden sadece ayarlanmış olan gerçeği kullanmak, bir hatanın oluştuğuna dair yeterince iyi bir gösterge değildir. Bir hatanın olup olmadığını öğrenmek için bireysel çağrıların sonuçlarını kontrol etmelisiniz.
jxh

1

Gerçekten ilgileniyorsanız, her iki işlev çağrısı için hata kontrolü yapabilirsiniz.

errno = 0;
double x = strtod(str1, NULL);
if (errno)
    // strtod"  failed
else
    // "strtod" succeeded

long y = strtol(str2, NULL);
if (errno)
    // "strtol" failed
else
    // "strtol" succeeded

Bir işlemde mach işlevinin nasıl çağrıldığını asla bilemediğimiz için, lib her işlev çağrısı için nasıl hatalar ayarlayabilir?


1

Hatayı keyfi olarak değiştirmek, bir istisnayı 'yakalamak ve yutmak' ile benzerdir. Bir programın farklı katmanları boyunca yayılacak ve nihayet arayanın ya istisnayı bir şekilde yakalayıp yanıtlayacağı ya da paranın basit bir şekilde geçeceği bir noktaya ulaşacak istisnalar bulunmadan önce, hatalar vardı. Errno'yu değiştirmemek, eğer bir şekilde işlemezseniz, geçersiz kılmazsanız, alakasız davranmazsanız, hata, bu ilk nesil hata işleme yayılma paradigması için gereklidir.

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.