次の方法で共有


ベスト プラクティスと例 (SAL)

ほとんどのソース・コードのコメントの言語 (SAL) から派生し、一般的な問題を回避する方法を次に示します。

_In_

関数が要素に書き込むことが想定されている場合 _In_の代わりに _Inout_ を使用します。これは、以前から SAL マクロへの自動変換のケースで特に関連しています。SAL 前に、多くのプログラマは、これらの名前の IN、OUT、IN_OUT、またはバリアントというコメント マクロとしてマクロを使用しました。SAL にこれらのマクロを変換することが推奨されていますが、このは、元のプロトタイプが書き込まれ、古いマクロは、コードの処理を反映する可能性があるため、コードが変更される可能性があるため、変換する場合は注意するようにせき立てます。例が、多くの場合、がに設定されているため、コンマの不適切な方向に OPTIONAL のコメントのマクロについて特に注意してください。

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

呼び出し元が _In_opt_ か _Out_opt_の代わりに使用 _In_ null ポインター、または _Out_ を渡すと、許されなければ。これはパラメーターをチェックし、エラーを返す関数の場所でない場合に null の場合に適用されます。検証関数を使用して予期しない NULL と戻り値のパラメーターは適切に、防御的なコーディング手法ですが、パラメーターのコメントが省略可能な型 (_Xxx_opt_) であることを意味しません。

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

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

_Pre_defensive_と_Post_defensive_

関数が信頼の境界にある場合、_Pre_defensive_ のコメントを使用することをお勧めします。 "防御された" 修飾子は、呼び出しの時点で、インターフェイスが厳密にチェックする必要のある実装の本体で不適切なパラメーターが渡される可能性があることを想定する必要があることを示すために、特定のコメントを変更します。このケースでは、呼び出し元がパラメーターが null である可能性があります。最初に NULL 用のコードをチェックインせずにポインターを逆参照しようとすると、フラグが設定されたことに NULL を渡すと、エラーが発生する、関数の本体は、分析することを示すには、_In_ _Pre_defensive_ は信頼の境界での使用を推奨されています。_Post_defensive_ のコメントは、信頼された呼び出し元が元で想定される信頼できないコードの呼び出し元のコードにあるコールバックでも使用できるようになります。

_Out_writes_

次の例では _Out_writes_の一般的な使用法を次に示します。

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

バッファーがあることを _Out_writes_ コメントは示します。これは cb バイトを終了時に初期化最初のバイトを、代入されます。このコメントは、問題なく、割り当てられているサイズを表現できると便利です。ただし、要素が関数によって初期化または、全体。

次の例は、完全に初期化一部のバッファーの実際のサイズを指定する 3 とおりの正しい方法。

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

_Out_ PSTR を使用すると、ほぼ必ず正しくない。これは、文字バッファーへのポインターが、それを持つことが NULL で終わる出力パラメーターとして解釈されます。

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

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

_In_ PCSTR のようなコメントは、共通と便利です。この例では、入力に _In_ の事前条件が NULL で終わる文字列の特定を使用すると、終端の null を含む文字列を指定します。

_In_ WCHAR* p

_In_ WCHAR* p は、1 文字を指す入力ポインターがある p ことを通知します。ただし、ほとんどの場合、これは、目的の仕様ではありません。代わりに、意図したとおりに、NULL で終わる配列仕様です。; これを行うには、_In_ PWSTRを使用します。

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

// Correct
void Func2(_In_ PWSTR wszFileName);

NULL で終わるの適切な仕様が見つかりませんが一般的です。次の例に示すように型をに置き換えるために STR の正しいバージョンを使用します。

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

パラメーターがポインターであり、ポインターが指し示す要素の値の範囲を指定するには、_Out_range_の代わりに _Deref_out_range_ を使用します。次の例では、*pcbFilled の範囲と表現されますが、エンティティ pcbFilled。

// 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) は、ツールに厳密に、_Out_writes_to_(cbSize,*pcbFilled)から推論できますが、期すために、ここに示されているためです。

_When_の正しくないコンテキスト

もう一つのよくある間違いを前提条件については後条件の評価を使用することです。次の例では、_Requires_lock_held_ は事前条件です。

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

式 result は、前の状態で使用できない状態後の値を示します。

_Success_に調整します。

戻り値がゼロ以外であると関数が成功した場合は return == TRUEの代わりに成功状態として return != 0 を使用します。は、必ずしもコンパイラが TRUEに提供する実際の値は、等価ではありません。_Success_ へのパラメーターは式で、次の式は等価として評価されます: パラメーターなしの比較または return != 0、return != falsereturn != FALSE と return。

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

参照変数

参照変数の場合、SAL の以前のバージョンでは、コメントのターゲットとして暗黙的なポインターを使用し、参照変数にアタッチされたコメントに __deref の追加が必要です。このバージョンは、オブジェクト自体を使用し、追加の _Deref_は必要ではありません。

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

戻り値のコメント

次の例は、一般的な問題の値のコメントをと引き換えに示します。

// Incorrect
_Out_opt_ void *MightReturnNullPtr1();

// Correct
_Ret_maybenull_ void *MightReturnNullPtr2();

この例では、_Out_opt_ は、ポインターが必須条件の一部として null である可能性があることを通知します。ただし、事前条件は戻り値に適用できません。この場合、正しいコメントは _Ret_maybenull_です。

参照

関連項目

関数パラメーターおよび戻り値の注釈設定

関数の動作に注釈を付ける

構造体とクラスに注釈を付ける

ロック動作に注釈を付ける

注釈を適用するタイミングと場所の指定

組み込み関数

概念

SAL について

その他の技術情報

SAL 注釈を使って C/C++ のコード障害を減らす方法