이 험난한 세상에서어어~

Understanding SAL (_In_, _Out_, _Out_opt_) 본문

카테고리 없음

Understanding SAL (_In_, _Out_, _Out_opt_)

토끼띠NJW 2023. 9. 15. 20:43

The _In_ Annotation

_In_ 어노테이션은 무조건 읽기를 표시한다. 가장 일반적인 실수는 _Inout_ 어노테이션 대신 _In_을 쓰는 것다.

아래는 공식 문서에 작성되어 있는 코드이다. 아래를 보고 이해해보자.

void InCallee(_In_ int *pInt)
{
   int i = *pInt;
}

void GoodInCaller()
{
   int *pInt = new int;
   *pInt = 5;

   InCallee(pInt);
   delete pInt;
}

void BadInCaller()
{
   int *pInt = NULL;
   InCallee(pInt); // pInt should not be NULL
}

먼저 InCallee 함수에서 _In_ 어노테이션을 붙여서 pInt라는 포인터를 파라미터로 받았다. 그리고 정수형 변수 i에 pInt가 가리키는 주소의 값을 지칭한다.

 

GoodInCaller 함수를 보면 pInt 포인터에 int형 저장 공간을 확보하고 해당 저장 공간에 5를 넣어준다. 그리고 InCallee에다가 포인터 변수 pInt를 넣어준 다음 pInt의 메모리를 해제해준다. 

 

BadInCaller 함수에는 pInt에다가 NULL을 넣어준다. 이렇게 하면 문제가 생기는데, 그 이유는 _In_ 어노테이션이 붙은 파라미터는 무조건 값을 명시해줘야 하기 때문이다.

The _In_opt_ Annotation

_In_ 어노테이션과 유사하지만 NULL을 지원한다. 

void GoodInOptCallee(_In_opt_ int *pInt)
{
   if(pInt != NULL) {
      int i = *pInt;
   }
}

void BadInOptCallee(_In_opt_ int *pInt)
{
   int i = *pInt; // Dereferencing NULL pointer 'pInt'
}

void InOptCaller()
{
   int *pInt = NULL;
   GoodInOptCallee(pInt);
   BadInOptCallee(pInt);
}

위의 코드를 보면  InOptCaller에서 *pInt에 NULL을 넣는 걸 알 수 있다. NULL을 넣었기 때문에 무조건 null인지 아닌지를 확인해줘야 한다. 해당 내용은 GoodInOptCallee와 BadInOptCallee를 비교해보면 알 수 있다.

The _Out_ Annotation

_Out_  에서는 반환하기 전에 무조건 명시가 되어 있어야 한다.

void GoodOutCallee(_Out_ int *pInt)
{
   *pInt = 5;
}

void BadOutCallee(_Out_ int *pInt)
{
   // Did not initialize pInt buffer before returning!
}

void OutCaller()
{
   int *pInt = new int;
   GoodOutCallee(pInt);
   BadOutCallee(pInt);
   delete pInt;
}

GoodOutCallee를 보면 명시해주는 것을 알 수 있다.