[SafeInt] C++에서 안전한 정수 연산을 하자 - 1

Visual C++ 10 2011. 2. 14. 09:00 Posted by 알 수 없는 사용자
#include <iostream> 

int main()

{

           unsigned int X1 = 1234567;

           unsigned int X2 = 1234567;

           unsigned int X3 = X1 * X2;

           std::cout << "X3 = " << X3 << std::endl;

 

           unsigned __int64 BigX1 = 1234567;

           unsigned __int64 BigX2 = 1234567;

           unsigned __int64 BigX3 = BigX1 * BigX2;

           std::cout << "BigX3 = " << BigX3 << std::endl;

 

           getchar();

           return 0;

}

 

<코드1>를 실행하면 변수 X3 BigX3의 값이 서로 같을까요? 혹시 같다고 생각하시는 분들은 unsigned int의 최대 값이 얼마인지 MSDN에서 검색해 보세요... 네 결과는 서로 다릅니다. 둘 다 계산에 사용하는 값은 같지만 결과가 다르게 나오는 이유는 unsigned int로는 X1 X2를 곱해서 나온 값을 보관할 수 없기 때문입니다. X3 = X1 * X2에서 X3는 오버플로우가 발생하여 상위 비트가 삭제되어 올바른 계산 값이 저장되지 않습니다.

 

<코드 1의 결과>


 

<코드 1>의 코드는 사실 별로 길지 않은 코드이기 때문에 실행하기 전에 오버플로우가 발생하리라는 것을 충분히 예상할 수 있고, 혹은 실행 후에 X3의 값이 예상하지 못한 값이 들어가 있어도 문제를 쉽게 파악할 수 있을 것입니다. 그러나 우리가 만드는 애플리케이션은 복잡하고 긴 코드를 가지고 있습니다. <코드 1>과 같은 오버플로우에 의해서 버그가 발생하면 쉽게 버그를 찾기 힘들고 특히 오버플로우에 의해 애플리케이션이 오 동작하여 크래쉬가 발생할 수도 있습니다.

 

안전한 애플리케이션을 만들기 위해서는 안전한 코드를 만들어야 합니다. 보통 안전한 코드를 생각하면 포인터 조작과 문자열 조작을 주로 중요하게 다루고 VC++에서도 안전한 문자열 조작 위해 ‘_s’가 붙은 문자열 조작함수를 사용하도록 VC++에서 종용하고 있어서 요즘은 대 부분 이것을 사용하고 있습니다. 그러나 정수 계산에 대해서는 안전한 코드를 위해 지원해 주는 것이 없었습니다.

 


SafeInt 란?

VC++ 10에서는 안전한 정수 계산을 위해서 새로운 라이브러리를 지원해 줍니다. 이 라이브러리의 이름은 SafeInt 입니다. SafeIntC++의 템플릿으로 만들어서 char 형에서 __int64 형까지 8비트에서 64비트 사이의 크기를 가진 모든 정수 형을 사용할 수 있습니다.

SafeInt 라이브러리를 사용하면 결과를 담을 변수의 형 보다 큰 정수 값 연산을 하거나 0으로 나누기 연산을 할 때 발생하는 오버플로우를 감지 할 수 있습니다.


 

SafeInt 사용

SafeInt를 사용하기 위해서는 헤더 파일 safeint.h 를 포함하고 msl::utilities 이름 공간을 선언해야 합니다.


 < 코드 2 >

#include <iostream>

#include <safeint.h>

using namespace msl::utilities;

 

int main()

{

           SafeInt<unsigned int> X1(1234567);

          

           unsigned int x2 = 1234567;

           SafeInt<unsigned int> X2(x2);


           SafeInt<unsigned int> X3 = X1 * X2;     

        

           getchar();

           return 0;

}

 

<코드 2>를 디버그 모드에서 실행하면 아래와 같은 ASSERT 메시지가 발생합니다.


 

이유는 오버플로우가 발생했기 때문입니다. 그러나 릴리즈 모드에서는 ASSERT 메시지가 발생하지 않습니다. 다만 크래쉬가 발생합니다. -_-;;

오버플로우에 의해서 디버그 모드에서는 ASSERT 메시지, 릴리즈 모드에서는 크래쉬가 발생하는 이유는

SafeInt<unsigned int> X3 = X1 * X2;

에서 예외가 발생하기 됩니다. SafeInt를 사용하는 경우 오버플로우가 발생하면 예외를 발생시키기 때문에 try{} catch{}로 예외를 처리해 주지 않으면 안됩니다. 예외 처리가 올바르게 하면 오버플로우가 발생했을 때 발생하는 문제를 올바르게 대처하던가 어디에서 어떻게 오버플로우가 발생했는지 쉽게 알 수 있습니다.

 

그리고 SafeInt는 일반 정수형과 같이 연산을 할 수도 있다.

<코드 3>

#include <iostream>

#include <safeint.h>

 

using namespace msl::utilities;

 

int main()

{

           SafeInt<unsigned int> X1(1234567);

          

           unsigned int X2 = 123;

           SafeInt<unsigned int> X3 = X1 * X2;

          

           getchar();

           return 0;

}


 

이번 회에는 간단하게 정수 연산 시의 오버플로우 문제와 SafeInt가 무엇인지, SafeInt의 간단한 사용 방법만 설명하였습니다.

다음 회는 <코드 2>에서 발생하는 예외를 어떻게 처리하는지 설명하겠습니다.