Aracılığıyla paylaş


En İyi Yöntemler ve Örnekler (SAL)

Burada, Kaynak Kod Ek Açıklama Dili'nin (SAL) dışında en iyi şekilde almak ve bazı yaygın sorunlardan kaçınmak için bazı yöntemler vardır.

_İçinde_

Eğer işlev öğeye yazmak için varsayılırsa, _In_ yerine _Inout_ kullanın.Bu, eski makroları SAL'a otomatik dönüştürme durumlarında özellikle uygundur.SAL öncesinde, birçok programcı makroları yorumlar olarak kullanılır— IN, OUT, IN_OUT olarak adlandırılmış veya bu adların türevleri makro komutları olarak adlandırılır.Ancak bu makroları SAL'a dönüştürmenizi öneririz, ayrıca dönüştürürken dikkatli olmanız için yönlendiririz; çünkü kod, orijinal prototip yazıldığından beri değişmiştir ve eski makrolar, kodun yaptıklarını daha fazla yansıtmaz.Özellikle OPTIONAL yorum makrosu hakkında dikkatli olun, çünkü o sık sık yanlış yerleştirilir—örneğin, virgülün yanlış tarafında olması.

// Incorrect
void Func1(_In_ int *p1)
{
    if (p1 == NULL) 
        return;

    *p1 = 1;
}

// Correct
void Func2(_Inout_ PCHAR p1)
{
    if (p1 == NULL) 
        return;

    *p1 = 1;
}

_opt_

Eğer çağırana, null bir işaretçiyi geçmesi için izin verilmiyorsa, _In_opt_ veya _Out_opt_ yerine _In_ veya _Out_ kullanın.Bu, parametrelerini denetleyen bir işlev için uygulanır ve eğer olmaması gerektiğinde NULL ise bir hata döndürür.İşleve sahip olmak beklenmeyen NULL için parametrelerini kontrol etse ve nazikçe geri dönse de, savunma kodlama denemesidir, parametre ek açıklaması isteğe bağlı bir tür için olabilir (_Xxx_opt_) anlamına gelmez.

// Incorrect
void Func1(_Out_opt_ int *p1)
{
    *p = 1;
}

// Correct
void Func2(_Out_ int *p1)
{
    *p = 1;
}

_Pre_defensive_ ve _Post_defensive_

Güven sınırında bir işlev görüntülenirse, kullanmanızı öneririz _Pre_defensive_ ek açıklama."Savunma" değiştiricisi çağrı noktasında belirtmek için bazı ek açıklamaları değiştirir, arayüz kesinlikle denetlenmelidir; ancak o, uygulama gövdesinde geçirilen yanlış parametreleri varsayar.Bu durumda, _In_ _Pre_defensive_ arayan bir kullanıcının null geçirmek çalışırsa bir hata iletisi alır rağmen işlev gövdesi parametresi null olabilir ve işaretçi null için ilk onu kontrol etmeden de-reference girişimleri bayraklı sanki çözümlenmesi olduğunu belirtmek için bir güven sınırında tercih edilir.A _Post_defensive_ ek açıklama da burada güvenilir parti arayan olarak kabul edilir ve güvenilmeyen kod adı verilen kod geri çağırmalar kullanmak için kullanılabilir.

_Out_writes_

Aşağıdaki örnek _Out_writes_ için ortak bir kötüye kullanımını göstermiştir.

// Incorrect
void Func1(_Out_writes_(size) CHAR *pb, 
    DWORD size
);

Ek açıklama _Out_writes_ bir arabelleğe sahip olduğunuzu belirtir.O, çıkışta başlatılan ilk bayt ile ayrılan cb baytlarına sahiptir.Bu ek açıklama, kesinlikle yanlış değildir ve ayrılan boyutu ifade etmek için yardımcıdır.Ancak, işlev tarafından kaç öğenin başlatıldığını söylemez.

Bir sonraki örnek, arabelleğin başlatıldığı bölümünün tam boyutunu tam olarak belirtmek için üç doğru yolu gösterir.

// Correct
void Func1(_Out_writes_to_(size, *pCount) CHAR *pb, 
    DWORD size,
    PDWORD pCount
);

void Func2(_Out_writes_all_(size) CHAR *pb, 
    DWORD size
);

void Func3(_Out_writes_(size) PSTR pb, 
    DWORD size
);

_Out_ PSTR

Bir _Out_ PSTR kullanımı, hemen hemen her zaman yanlış olur.Bu, bir karakter arabelleğini işaret eden çıktı parametresine sahip olarak yorumlanır ve NULL sonlandırılmış olur.

// Incorrect
void Func1(_Out_ PSTR pFileName, size_t n);

// Correct
void Func2(_Out_writes_(n) PSTR wszFileName, size_t n);

Bir _In_ PCSTR gibi bir ek açıklama yaygın ve kullanışlıdır.NULL sonlandırmaya sahip olan girdi dizesini gösterir; çünkü _In_ 'in önkoşulu NULL sonlandırma dizesinin tanımlanmasını sağlar.

_In_ WCHAR* p

_In_ WCHAR* p, bir karakter gösteren p girdi işaretçisi olduğunu söyler.Ancak, çoğu durumda, bu büyük olasılıkla amaçlanmış bir belirtme değildir.Bunun yerine büyük bir olasılıkla ne hedeflendiği, NULL ile sonlandırılmış bir dizinin belirtimidir; bunu yapmak için _In_ PWSTR kullanın.

// Incorrect
void Func1(_In_ WCHAR* wszFileName);

// Correct
void Func2(_In_ PWSTR wszFileName);

NULL sonlandırmasının eksik uygun belirtimi yaygındır.Aşağıdaki örnekte gösterildiği gibi, tür değiştirmek için uygun STR sürümünü kullanın.

// Incorrect
BOOL StrEquals1(_In_ PCHAR p1, _In_ PCHAR p2)
{
    return strcmp(p1, p2) == 0;
}

// Correct
BOOL StrEquals2(_In_ PSTR p1, _In_ PSTR p2)
{
    return strcmp(p1, p2) == 0;
}

_Out_range_

Eğer parametre, bir işaretçiyse ve işaretçi tarafından gösterilen öğenin değer aralığını anlatmak istiyorsanız, _Out_range_ yerine _Deref_out_range_ kullanın.Aşağıdaki örnekte, *pcbFilled'in değer aralığı ifade edilmiştir, pcbFilled'in değil.

// Incorrect
void Func1(
    _Out_writes_bytes_to_(cbSize, *pcbFilled) BYTE *pb, 
    DWORD cbSize, 
    _Out_range_(0, cbSize) DWORD *pcbFilled
);

// Correct
void Func2(
    _Out_writes_bytes_to_(cbSize, *pcbFilled) BYTE *pb, 
    DWORD cbSize, 
    _Deref_out_range_(0, cbSize) _Out_ DWORD *pcbFilled 
);

_Deref_out_range_(0, cbSize), bazı araçlar için kesinlikle gerekli değildir çünkü o, _Out_writes_to_(cbSize,*pcbFilled)'dan anlaşılabilir; ancak eksik kalmaması için aşağıda gösterilmiştir.

_When_'de Yanlış Bağlam

Başka bir yaygın yanlışlık, önkoşullar için sonraki durum değerlendirmesi kullanmaktır.Aşağıdaki örnekte _Requires_lock_held_, bir önkoşuldur.

// Incorrect
_When_(return == 0, _Requires_lock_held_(p->cs))
int Func1(_In_ MyData *p, int flag);

// Correct
_When_(flag == 0, _Requires_lock_held_(p->cs))
int Func2(_In_ MyData *p, int flag);

Bir result ifadesi, önceki durumda kullanılabilir olmayan bir sonraki durum değerini gösterir.

_Success_'te TRUE

Eğer işlev, dönüş değeri sıfır olmadığında başarılı olursa; başarı koşulu olarak return == TRUE yerine return != 0 kullanın.Sıfır olmayanın; derleyici TRUE için sağlandığı gerçek değere mutlaka denk olduğu anlamına gelmez.Parametre _Success_ bir ifadesidir ve aşağıdaki ifadeler eşdeğer olarak değerlendirilir: return != 0, return != false, return != FALSE, ve return karşılaştırmaları veya parametreleri ile.

// Incorrect
_Success_(return == TRUE, _Acquires_lock_(*lpCriticalSection))
BOOL WINAPI TryEnterCriticalSection(
  _Inout_ LPCRITICAL_SECTION lpCriticalSection
);

// Correct
_Success_(return != 0, _Acquires_lock_(*lpCriticalSection))
BOOL WINAPI TryEnterCriticalSection(
  _Inout_ LPCRITICAL_SECTION lpCriticalSection
);

Başvuru Değişkeni

Bir başvuru değişkeni için SAL'ın önceki sürümleri, ek açıklama hedefi olarak kastedilen işaretçi kullanılır ve bir başvuru değişkeni için eklenmiş bir ek açıklama için bir __deref eklemesi gereklidir.Bu sürüm kendi nesnesini kullanır ve _Deref_ ek açıklaması gerekli değildir.

// Incorrect
void Func1(
    _Out_writes_bytes_all_(cbSize) BYTE *pb, 
    _Deref_ _Out_range_(0, 2) _Out_ DWORD &cbSize
);

// Correct
void Func2(
    _Out_writes_bytes_all_(cbSize) BYTE *pb, 
    _Out_range_(0, 2) _Out_ DWORD &cbSize
);

Dönüş Değerlerindeki Ek Açıklamalar

Aşağıdaki örnek dönüş değeri ek açıklamalarındaki ortak bir sorunu gösterir.

// Incorrect
_Out_opt_ void *MightReturnNullPtr1();

// Correct
_Ret_maybenull_ void *MightReturnNullPtr2();

Bu örnekte _Out_opt_, işaretçinin önkoşulun bir parçası olarak NULL olabileceğini söylüyor.Ancak önkoşullar, dönüş değeri için uygulanamaz.Bu durumda doğru ek açıklama _Ret_maybenull_'dır.

Ayrıca bkz.

Başvuru

İşlev Parametrelerini ve Dönüş Değerlerini Açıklama

İşlev Davranışını Yorumlama

Yapıları ve Sınıfları Yorumlama

Kilitlenme Davranışını Yorumlama

Açıklamanın Ne Zaman ve Nereye Uygulanacağını Belirtme

İç İşlevler

Kavramlar

sal anlama

Diğer Kaynaklar

c/C++ kod hataları azaltmak için sal açıklamaları kullanma