Aracılığıyla paylaş


En iyi yöntemler ve örnekler (SAL)

Kaynak Kodu Ek Açıklama Dili'ni (SAL) en iyi şekilde kullanmanın ve bazı yaygın sorunlardan kaçınmanın bazı yolları aşağıdadır.

_In_

İşlevin öğesine yazması gerekiyorsa yerine kullanın _Inout__In_. Bu, eski makrolardan SAL'ye otomatik dönüştürme durumlarında geçerlidir. SAL'den önce, birçok programcı makroları açıklama olarak kullanıyordu; bu adların , OUT, IN_OUTveya değişkenlerini adlandıran INmakrolar. Bu makroları SAL'ye dönüştürmenizi önersek de, özgün prototip yazıldığından ve eski makro artık kodun ne yaptığını yansıtmadığından kod değişmiş olabileceğinden, bunları dönüştürürken dikkatli olmanız önerilir. Özellikle açıklama makrosunun OPTIONAL yanlış yerleştirilmesine (örneğin, virgülünün yanlış tarafına) dikkat edin.

#include <sal.h>

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

    *p1 = 1;
}

// Correct
// _Out_opt_ because the function tolerates NULL as a valid argument, i.e.
// no error is returned. If the function didn't check p1 for NULL, then
// _Out_ would be the better choice
void Func2(_Out_opt_ PCHAR p1)
{
    if (p1 == NULL)
        return;

    *p1 = 1;
}

_opt_

Çağıranın null işaretçi geçirmesine izin verilmiyorsa veya yerine _In_opt_ veya _Out__Out_opt_kullanın_In_. Bu, parametrelerini denetleen ve olmaması gereken bir durumda hata NULL döndüren bir işlev için bile geçerlidir. İşlevin parametresini beklenmeyen NULL ve düzgün bir şekilde döndürmesini denetlemesi iyi bir savunma kodlama uygulaması olsa da, parametre ek açıklamasının isteğe bağlı bir türde (_*Xxx*_opt_) olabileceği anlamına gelmez.

#include <sal.h>

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

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

_Pre_defensive_ ve _Post_defensive_

Bir işlev güven sınırında görünüyorsa, ek açıklamayı _Pre_defensive_ kullanmanızı öneririz. "Savunma" değiştirici, çağrı noktasında arabirimin kesin olarak denetlenmesi gerektiğini, ancak uygulama gövdesinde yanlış parametrelerin geçirilebileceğini varsayması gerektiğini belirtmek için bazı ek açıklamaları değiştirir. Bu durumda, _In_ _Pre_defensive_ bir çağıranın geçirmeyi NULLdenediği takdirde bir hata almasına rağmen, işlev gövdesinin parametresi olabilir NULLgibi analiz edilebileceğini ve işaretçiyi önce denetlemeden başvuruyu kaldırma girişimlerinin bayraklı olduğunu belirtmek için NULL güven sınırında tercih edilir. _Post_defensive_ Ayrıca, güvenilen tarafın çağıran olduğu varsayıldığı ve güvenilmeyen kodun kod olarak adlandırıldığı geri aramalarda kullanılmak üzere bir ek açıklama da kullanılabilir.

_Out_writes_

Aşağıdaki örnekte yaygın bir kötüye kullanımı gösterilmektedir _Out_writes_.

#include <sal.h>

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

Ek açıklama _Out_writes_ , arabelleğe sahip olduğunuzu gösterir. Çıkışta ilk bayt başlatılırken ayrılan baytlar vardır cb . Bu ek açıklama kesinlikle yanlış değildir ve ayrılan boyutu ifade etmek yararlı olur. Ancak işlevin kaç öğe başlatdığı anlatılmıyor.

Sonraki örnekte, arabelleğin başlatılan bölümünün tam boyutunu tam olarak belirtmenin üç doğru yolu gösterilmektedir.

#include <sal.h>

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

kullanımı _Out_ PSTR neredeyse her zaman yanlıştır. Bu birleşim, karakter arabelleğine işaret eden bir çıkış parametresine sahip olduğu ve arabelleğin null olarak sonlandırıldığı olarak yorumlanır.

#include <sal.h>

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

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

gibi _In_ PCSTR bir ek açıklama yaygın ve kullanışlıdır. önkoşulu null ile sonlandırılan dizenin tanınmasına izin verdiğinden _In_ , null sonlandırması olan bir giriş dizesini işaret eder.

_In_ WCHAR* p

_In_ WCHAR* p bir karaktere işaret eden bir giriş işaretçisi p olduğunu söyler. Ancak, çoğu durumda, bu muhtemelen amaçlanan belirtim değildir. Bunun yerine, büyük olasılıkla amaçlanan, null olarak sonlandırılan bir dizinin belirtimidir; bunu yapmak için kullanın _In_ PWSTR.

#include <sal.h>

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

// Correct
void Func2(_In_ PWSTR wszFileName);

Null sonlandırmanın uygun belirtiminin eksik olması yaygın bir durumdur. Aşağıdaki örnekte gösterildiği gibi türü değiştirmek için uygun STR sürümü kullanın.

#include <sal.h>
#include <string.h>

// 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_

Parametre bir işaretçiyse ve işaretçi tarafından işaret edilen öğenin değerinin aralığını ifade etmek istiyorsanız yerine kullanın _Deref_out_range__Out_range_. Aşağıdaki örnekte , *pcbFilled aralığı pcbFilled değil ifade edilir.

#include <sal.h>

// 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 kesin olarak gerekli değildir çünkü uygulamasından _Out_writes_to_(cbSize,*pcbFilled)çıkarılabilir, ancak tamlık için burada gösterilir.

içinde yanlış bağlam _When_

Bir diğer yaygın hata da önkoşullar için durum sonrası değerlendirmeyi kullanmaktır. Aşağıdaki örnekte önkoşul _Requires_lock_held_ verilmiştir.

#include <sal.h>

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

İfade return , durum öncesi durumda bulunmayan bir post-state değerine başvurur.

_Success_ içinde TRUE

Dönüş değeri sıfır olmadığında işlev başarılı olursa, yerine başarı koşulu return == TRUEolarak kullanınreturn != 0. Sıfır olmayan, derleyicinin için TRUEsağladığı gerçek değere denklik anlamına gelmez. parametresi _Success_ bir ifadedir ve aşağıdaki ifadeler eşdeğer olarak değerlendirilir: return != 0, return != false, return != FALSEve return parametresi veya karşılaştırması yoktur.

// 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'nin önceki sürümü ek açıklama hedefi olarak zımni işaretçiyi kullandı ve bir başvuru değişkenine eklenmiş ek açıklamalara eklenmesini gerektiriyordu __deref . Bu sürüm nesnesinin kendisini kullanır ve gerektirmez _Deref_.

#include <sal.h>

// 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ğerlerinde ek açıklamalar

Aşağıdaki örnekte, dönüş değeri ek açıklamalarında sık karşılaşılan bir sorun gösterilmektedir.

#include <sal.h>

// Incorrect
_Out_opt_ void *MightReturnNullPtr1();

// Correct
_Ret_maybenull_ void *MightReturnNullPtr2();

Bu örnekte, _Out_opt_ işaretçinin önkoşulun bir parçası olabileceğini NULL söyler. Ancak, önkoşullar dönüş değerine uygulanamaz. Bu durumda, doğru ek açıklama şeklindedir _Ret_maybenull_.

Ayrıca bkz.

C/C++ kod hatalarını azaltmak için SAL ek açıklamalarını kullanma
SAL'yi Anlama
İşlev parametrelerine ve dönüş değerlerine açıklama ekleme
İşlev davranışına açıklama ekleme
Yapılara ve sınıflara açıklama ekleme
Kilitleme davranışına açıklama ekleme
Ek açıklamanın ne zaman ve nereye uygulanacağını belirtme
İç işlevler