다음을 통해 공유


모범 사례 및 예제(SAL)

소스 코드 주석 언어 (SAL)에서 활용 하 고 몇 가지 일반적인 문제를 방지 하는 몇 가지는 다음과 같습니다.

_In_

함수 요소를 작성 하는 경우 사용 _Inout_ 대신 _In_.이 오래 된 매크로에서 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_

호출자가 null 포인터를 전달할 수 있는 경우 사용 _In_ 또는 _Out_ 대신 _In_opt_ 또는 _Out_opt_.이 매개 변수를 확인 하 고 않아야 하면 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_ 주석."방어" 한정자를 호출할 때를 나타내기 위해 특정 주석을 수정, 인터페이스를 엄격 하 게, 확인 해야 하지만 구현 본문에는 잘못 된 매개 변수를 전달 될 수 있는 가정 합니다.이런 경우 _In_ _Pre_defensive_ 호출자가 NULL을 전달 하려고 하면 오류가 나타납니다 있지만 매개 변수가 NULL 수 있습니다 첫 번째 NULL을 검사 하지 않고 포인터를 de-reference 하려고 플래그가 지정 됩니다 처럼 함수 본문이 분석 됩니다 나타내는 신뢰 경계에 것이 좋습니다.A _Post_defensive_ 주석에서 사용할 수 있는 신뢰할 수 있는 타사 호출자로 간주 됩니다 하 고 호출된 된 코드가 신뢰할 수 없는 코드는 콜백 이기도 합니다.

_Out_writes_

다음 예제는 일반적인 오용의 _Out_writes_.

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

주석 _Out_writes_ 버퍼가 있는지를 나타냅니다.가 cb 바이트 종료 시 초기화 하는 첫 번째 바이트를 할당 합니다.이 주석 엄격 하 게 잘못 되어 할당 된 크기를 표현 하는 것이 좋습니다.그러나이 요소는 해당 함수에 의해 초기화 됩니다 알 수 없습니다.

다음 예제에서는 완전 하 게 초기화 부분 버퍼의 정확한 크기를 지정 하는 세 올바른 방법을 보여 줍니다.

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

PSTR _Out_

사용 하는 _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 는 일반적이 고 유용 합니다.입력된 문자열을 NULL 종료 하기 때문에를 가리키는의 전제 조건 _In_ NULL로 끝나는 문자열 인식이 있습니다.

_In_ WCHAR * p

_In_ WCHAR* p입력된에 대 한 포인터 임을 알리는 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_

매개 변수에 대 한 포인터입니다 가리키는 포인터에서 사용 하는 요소의 값의 범위를 표현할 경우 _Deref_out_range_ 대신 _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_에 잘못 된 컨텍스트

다른 일반적인 실수에 대 한 사전 post-state 평가 사용 하는 것.다음 예제에서 _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 post-state pre-state에서 사용할 수 없는 값을 참조 합니다.

_Success_에서 true로

반환 값이 아닌 경우에 함수가 성공적으로 실행 하는 경우 사용 return != 0 대신 성공 조건으로 return == TRUE.Nonzero 반드시 아닙니다 동등에 대 한 컴파일러를 제공 하는 실제 값을 TRUE.매개 변수를 _Success_ 다음 식은 동일한 것으로 평가 되 고 식: return != 0, return != false, return != 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 이해

기타 리소스

C/C++ 코드 오류를 줄이기 위한 SAL 주석 사용