F.R.I.D.A.Y.

예외처리 - assert 사용해보기 본문

DEV/C C++

예외처리 - assert 사용해보기

F.R.I.D.A.Y. 2019. 4. 12. 08:49
반응형

 

 많은 프로그래머들이 작성된 코드로 인해 발생하는 각종 문제들에 골머리를 앓고 있습니다. 현업 개발자뿐만 아니라 일반 학부생, 아마추어 개발자들도 예외는 아닌데요, 오늘 포스팅에서는 이러한 버그를 잡는 방법중 하나인 assert에 대해 알아봅니다.


버그의 종류

 먼저 버그의 종류를 알아보죠. 버그는 크게 두 가지 종류로 나뉩니다. 문법상 코드를 잘못 작성해 발생하는 문법오류(Syntax Error)와 문법상 문제는 없지만 예상치 않은 결과가 발생하는 논리적 오류(Logical Error)입니다. 여기서 잡기 힘든 코드는 논리적 오류입니다. 문법 오류의 경우 IDE의 인텔리센스가 잡아서 경고를 띄워주기도, 컴파일이 되지 않는 방법으로 사전에 알 수 있지만, 논리적 오류는 코드상 문제가 아니라 들어온 데이터, 수식상 문제 등으로 발생하기 때문에 한번 발생하면 경우에 따라선 어느 위치에서 오류가 발생했는지 알 수 없습니다.

 

 이번 포스팅은 아마도 논리적 오류를 잡는 방법에 대해 다룰것 같네요.


Assert

 아래 코드는 문제의 소지가 있습니다.

double foo(int val) {

	return 1.0 / val;
}

 val의 값이 0이라면 어떨까요? 1을 0으로 나누게 되면 컴퓨터는 계산 불가능으로 오류를 토해내고 프로그램을 종료합니다. 이 때, 프로그램을 만든 개발자는 어째서 프로그램이 죽었는지 알 수 없습니다. val 값이 무엇이 들어왔는지 알 수 없기 때문이죠. 따라서 assert를 사용해봅니다. assert는 assert.h 헤더에 define 되어있네요.

#include <assert.h> // assert()를 사용하기 위해 추가합니다.

double foo(int val) {
	assert(val);
	return 1.0 / val;
}

 이렇게 작성하면 val의 값이 0일 때 assert에서 오류를 잡아냅니다. 그리고 프로그램을 중단시키면서 해당 위치를 잡아줍니다. assert는 일반적인 코드가 true일때 작동하는것과 달리 결과가 false일 때 작동합니다. 꼭 참고하세요. 개인적으로 false는 거짓, 수식이 맞지 않음 등을 의미하니 false일 때 작동하도록 만든 것 같아요.

assert에 넣은 식과 해당 assert가 있는 파일-라인을 표시해줍니다.


어째서 사용할까

 이 글을 읽기 전에도 많은 오류를 접했을 수 있습니다. 버그가 발생했을 때 당신은 어떤 방법으로 버그를 찾아 해결했나요? 버그를 찾아 해결하는 방법은 많습니다. 그러나 한가지 질문하고 싶습니다. 만일 당신이 만든 프로그램을 릴리즈한다면 버그를 찾기 위해 작성했던 모든 코드를 처리할 수 있나요?

double foo(int val) { 
	if (val == 0) {
		printf("val 값은 0이면 안됩니다.");
	}
	return 1.0 / val;
}

 이렇게 출시할 프로그램의 버그를 찾기 위한 부가 코드를 우리는 지울 필요가 있습니다. 일일이 찾아 지우기엔 귀찮습니다. 그래서 assert를 사용합니다.

 assert는 한가지 특징을 가지고 있습니다. 컴파일 인수에 NDEBUG 옵션이 들어가면 assert는 작동을 하지 않습니다. 

NDEBUG가 define 되어있으면 일반적인 숫자 0으로 작동함을 알 수 있습니다. Visual Studio 2019

 이러한 특성으로 Release로 프로그램을 컴파일 할 때 컴파일 옵션에 NDEBUG를 작성해주면 됩니다. 따로 define 해서 작성할 필요가 없어요.


NDEBUG는 이곳에

 일반적으로 NDEBUG는 따로 파일상에 선언하지 않습니다. Release에서도 코드에 define된 것은 그대로 전처리되기 때문이죠.

#define NDEBUG // 이런 식으로 NDEBUG를 선언하지 않습니다.
#include <stdio.h>
#include <assert.h>

double foo(int val) { 
	assert(val != 0);
	return 1.0 / val;
}

int main(void) {
	foo(0);
	return 0;
}

 따라서 프로젝트에서 Release 모드에 대해 NDEBUG를 선언합니다.

 먼저 프로젝트 설정을 엽니다.

상단 프로젝트-속성을 누릅니다.

 속성 창의 상단 왼쪽, 구성 옵션이 Release인지 확인하세요. 만일 활성(Debug)나 Debug, 모든 구성이라면 Release를 선택해줍니다.

더보기

활성()

 활성()은 현재 컴파일할 옵션의 구성을 변경합니다. 프로젝트를 생성하면 기본적으로 Debug로 설정이됩니다. 컴파일시에 선택은 여기서 할 수 있습니다.

 Debug

 프로그램의 디버깅이 편하도록 각종 옵션이 달라붙어 컴파일되는 구성입니다. Release 구성으로 컴파일 했을 때보다 프로그램이 느리지만 디버깅시 프로그램이 쉽게 죽거나 다른 메모리 영역을 침범하지 못하도록 더 많은 메모리를 할당해 실행시킵니다. 뿐만 아니라 초기화되지 않은 변수에도 특정한 값을 넣어서 초기화 되지 않음을 알리기도 합니다. 만일 Debug 모드에선 잘 돌아가는데 Release로 변경해서 문제가 발생한다면 잘못된 코드를 작성했으니 코드를 잘 들여다보시기 바랍니다.

Release

 만든 프로그램을 정식 출시할 때 디버그에서 추가됐던 각종 옵션들이 제거된, 속도 최적화로 컴파일을 진행합니다.

구성이 Release인지 확인하세요.

 왼쪽 메뉴에서 [C/C++ - 명령출]을 클릭합니다. C/C++ 메뉴는 C파일이나 C++파일이 하나 이상 존재해야 활성화됩니다.

[C/C++ - 명령줄]로 이동합니다.

 추가 옵션(D)에 " /D NDEBUG "를 추가합니다. 이렇게 추가한 뒤 구성을 Release로 하여 컴파일을 진행하면 Release모드에서 컴파일을 진행하면 assert 기능이 수행되지 않습니다.


함께 보기.

 

코드 디버깅 with Visual Studio

 개발의 한 축, 디버깅 시작하며  개발에선 코드를 잘 작성하는 것이 중요합니다. 알고리즘과 더불어 코드 구조를 어떻게 짜느냐도 큰 부분을 담당하죠. 그러나 코드를 짜는 것에 있어, 항상 원

pang2h.tistory.com

 디버깅 잘해서 오류 없는 좋은 프로그램 많이 만드세요! :)

# index

728x90
반응형

'DEV > C C++' 카테고리의 다른 글

배열 선언을 x-y가 아니라 y-x순으로 작성하는 이유  (4) 2019.04.24
상수의 접미어  (0) 2019.04.13
숫자의 형변환  (2) 2019.04.11
변수 초기화  (0) 2019.04.06
부동소수점은 ==으로 비교하지 마세요  (6) 2019.04.05
Comments