본문 바로가기
개발/C++

안전한 정수 연산을 위해 SafeInt 를 써보자

by 램램이 2020. 7. 13.

SafeInt

사용 목적

SafeInt는 정수 연산에 있어서 발생할 수 있는 오버플로우를 방지하는 목적으로 사용

C++ 의 템플릿으로 만들어졌으며, char형에서 __Int64(long long)형 까지 모든 정수형 사용 가능

정수 연산을 통한 오버플로우나, 0으로 나누었을때 발생하는 오버플로우 감지 가능


기본 정수 자료형을 사용 했을시

예제 소스 - unsigned int

#include <iostream>


int main()

{

unsigned int X1 = 1234567;


unsigned int X2 = 1234567;


unsigned int X3 = X1 * X2;


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

}



실행 결과


위와 같이 unsigned int 형으로 계산 했을 때의 값은 정확한 값이 나오지 않는다.



정확히 계산된 값( 윈도우 계산기 사용 )


SafeInt를 사용했을 시

예제소스 - SafeInt<unsigned int>

#include <iostream>

#include <safeint.h>


using namespace msl::utilities;


int main()

{

SafeInt<unsigned int> X1(1234567);


SafeInt<unsigned int> X2(1234567);


SafeInt<unsigned int> X3 = X1 * X2;


}


똑같은 계산을 safeint를 적용하여 계산하면,



위와같은 오류를 내뱉는데, 이유는 오버플로우가 발생했기 떄문.


이렇게 오버플로우가 발생하면 바로 오류를 발생 시키기 때문에 

잘못 된 값이 들어간 상태로 코드가 진행 될 일을 방지 할 수 있다


잘못 된 값을 처리하는 방법은 여러가지가 있다.

1. try~catch를 사용한 예외 처리 방법

2. SafeInt의 기본 예외처리 클래스를 상속 받아서 원하는 방식으로 정의

3. SafeInt 함수 사용


1. try~catch 사용 예외처리

예제 소스

#include <iostream>

#include <safeint.h>


using namespace msl::utilities;


int main()


{

SafeInt<unsigned int> X1(1234567);

SafeInt<unsigned int> X2(1234567);

try

{

SafeInt<unsigned int> X3 = X1 * X2;

}


catch (SafeIntException e)

{

std::cout << "overflow!!. ErrorCode : " << e.m_code << std::endl;

}


}



실행결과

위와같이 오버플로우가 발생할시 SafeInt는 예외를 던지며 예외를 catch에서 처리할 수 있다.


2. SafeInt 예외처리 클래스

예제 소스

#include <iostream>

#include <safeint.h>


using namespace msl::utilities;


class MySafeIntException : public SafeIntException

{

public:

static void SafeIntOnOverflow()

{

std::cout << "Overflow 발생!" << std::endl;

}


static void SafeIntOnDivZero()

{

std::cout << "0으로 나누기 발생!" << std::endl;

}


};



int main()

{

SafeInt<unsigned int, MySafeIntException> X1(1234567);

SafeInt<unsigned int, MySafeIntException> X2(1234567);

SafeInt<unsigned int, MySafeIntException> X3 = X1 * X2;

SafeInt<unsigned int, MySafeIntException> X4 = X1 / 0;

}


실행결과


예외처리 기본 클래스를 사용하면 SafeInt 연산중 예외처리가 발생하면, 직접 정의한 함수가 호출이 된다.


예외처리시 중요한 점 

1. 예외처리 클래스는 SafeIntException 클래스를 상속 받아야함

2. static void 형으로 SafeIntOntOverflow()와 SafeIntOnDivZero()를 재정의


추가로 매번 SafeInt 인자로 MySafeIntException을 넘겨주었는데 매번 이렇게 넘겨주기 번거롭기 때문에

클래스 탬플릿 인자를 미리 정의 해 놓으면 보다 쉽게 적용 할 수 있다.


예제코드

#include <iostream>


#define _SAFEINT_DEFAULT_ERROR_POLICY MySafeIntException

#include <safeint.h>


using namespace msl::utilities;


class MySafeIntException : public SafeIntException

{

public:

static void SafeIntOnOverflow()

{

std::cout << "Overflow 발생!" << std::endl;

}


static void SafeIntOnDivZero()

{

std::cout << "0으로 나누기 발생!" << std::endl;

}


};



int main()

{

SafeInt<unsigned int> X1(1234567);

SafeInt<unsigned int> X2(1234567);

SafeInt<unsigned int> X3 = X1 * X2;

SafeInt<unsigned int> X4 = X1 / 0;

}



실행결과


실행 결과는 전과 같지만, SafeInt를 생성 하는 부분의 매개변수, #define으로 정의 된 부분이 달라진 것을 볼 수있다.


3. SafeInt 라이브러리 함수 사용

정수 오버플로우가 발생하지 않도록 단일 수치 연산을 보호하고 싶을때 사용.

3개이상의 수치연산을 진행 할 때는 함수를 반복해서 사용하기 보다는 SafeInt 클래스를 사용 하는 것이 더 효율적


함수

 설명

 SafeAdd


 두 개의 값을 더한다

 SafeCast


 다른 형으로 캐스팅한다

 SafeDivide


 두 개의 값으로 나눈다

 SafeEquals, SafeGreaterThan, SafeGreaterThanEquals, SafeLessThan, SafeLessThanEquals, SafeNotEquals

 2개의 값을 비교한다이 함수들을 사용하면 서로 형이 다른 

두 개의 값을 형 변환하지 않고 비교할 수 있다

 SafeModulus

 나머지를 구한다

 SafeMultiply

 두 개의 값을 곱한다.

 SafeSubtract

 두개의 값을 뺀다. 




예제소스

#include <iostream>

#include <safeint.h>


using namespace msl::utilities;


int main()

{

unsigned char X1 = 123;

unsigned char X2 = 200;

unsigned char X3 = 0;

if (false == SafeAdd(X1, X2, X3)) {

std::cout << "Overflow 발생!" << std::endl;

}


unsigned int A1 = 100;

short A2 = 101;

if (false == SafeEquals(A1, A2)) {

std::cout << "A1와 A2는 서로 다릅니다" << std::endl;

}


return 0;


}


실행결과


위와같이 SafeInt 라이브러리 함수들은 bool 값을 return 해준다.

따라서 함수내부 계산에서 오버플로우가 발생했을 때, false를 반환한다.

이를 이용하여 예외처리를 진행 하면된다.





https://vsts2010.tistory.com/530?category=143386

https://docs.microsoft.com/ko-kr/cpp/safeint/safeint-library?view=vs-2019

위 사이트를 참고하여 작성했습니다.

댓글