본문 바로가기

단언하기 (Assert) 본문

💘 C++/함수

단언하기 (Assert)

Hyonii 2022. 8. 20. 19:03

단언하기 (Assert)

프로그래밍 시간의 대부분은 사실 프로그래밍 그 자체보다는 디버깅에 소모가 많이 된다.

디버깅을 할 때도 컴파일러의 도움을 받을 수 있다면 훨씬 빠르게 디버깅할 수 있다.

여기서는 컴파일러 도움을 받을 때 사용하는 assert(단언하기)에 대해서 말씀드리겠다.


How to use Assert

사용법은 간단하다.

 

<cassert>를 include 해주고

assert라고 적어주고 그 안에 필요한 조건을 적는데

지금은 이 성질을 테스트하기 위해서 false를 적어보겠다.

debug 모드에서 실행시켜보면

 

런타임 에러가 발생을 하고

더 좋은건 Assertion failed라고 어디서 오류가 났는지 까지도 알려준다.

그런데 이 assert의 특징은 release 모드로 바꾸면 작동을 안 한다.

 

아무 일도 없는 듯이 넘어간다.

프로그래머가 테스트할 때 debug 모드에서만 작동하는 것이다.

 

여기서 NDEBUG가 정의되어있으면 assert가 작동을 안 한다.

 

Debug 모드에서는 _DEBUG가 정의되어있다.

assert라는 게 모드에 따라서 작동을 할 수도 있고 안 할 수도 있다는 것은 중요한 성질 중 하나다.


조금 더 이해하기 쉬운 예제를 들어보자.

 

int number가 있고

만약 이 number가 반드시 5여야만 한다면 이렇게 assert를 걸어버리는 것이다.

 

예를 들어 프로그램이  중간영역에서 어떤 과정을 거치는데

결과적으로 number가 5 여야지만 정상작동을 하는 거라고 가정해보자.

이런 것들을 assert 없이 주석만 남겨놓는다면

 

결국은 프로그래머가 문제가 생겼을 때 다시 와서'아 여기서 5여야 하는 건가?' 하고 찍어봐야 된다는 것이다.

굉장히 번거롭고 현실적으로 실용성이 없는 방식이다.

 

이것보다는 코멘트 남길 필요도 없이 assert를 박아 넣어 버리는 것이다.

그러면 debug 모드에서는 이 오류를 찾아준다.

 

release모드에서는 이 assert를 실행시키지 않는다.

왜냐하면 assert를 실행시키는 것도 연산량을 먹는 것이기 때문에 느려진다.

release모드에서는 가급적 프로그램이 빠르게 돌아야 하기 때문에 assert를 빼버린다.

 


예제를 한 개 더 보자.

이번에는 array를 사용해보자.

 

이때 my_array를 출력할 때 0 1 2 3 4는 괜찮은데

5를 넣을 때부터는 문제가 생긴다.

런타임 에러 난다.

 

그런데 이때 printValue 함수를 만들었다.

array<int, 5> 를 레퍼런스로 받고,

안전장치를 위해서 나름 const도 넣어주고,

인덱스로 const int& ix 를 넣어주는 함수를 짰다.

 

이 함수가 내가 만든 api라고 가정을 해보자.

다른 프로그래머들은 이 내용을 모르고 사용한다.

 

만약 다른 어떤 프로그래머가 printValue를 실행을 시키면서

my_array를 넣고 100을 넣으려는 시도를 한다고 생각해보자.

 

그럼 이 printValue 함수를 사용하는 사람들을 쫓아다니면서

매번 "여기에는 이 사이즈만큼만 넣어줘야 해요"라고 얘기하고 다닐 수는 없다.

그럴 때 assert를 사용한다.

 

일단 ix는 0 이상이어야 한다.

그리고 my_array.size() - 1 여야 한다.

이렇게 되면 debug 모드로 실행시켰을 때

 

런타임 에러가 나긴 하지만대신 어디서 어떤 것 때문에 문제가 생겼는지 알 수 있다.

그렇다면 다른 프로그래머는 '아 내가 100이라는 숫자를 넣은 게 너무 커서 여기서 assert가 발생을 했구나'

'내가 이렇게 하면 안 되겠구나' 생각을 할 수 있을 것이다.

 

이 두 개를 &&로 묶어서 하나로 합칠 수 있지만 그냥 두 개로 쪼개 쓰는 게 좋다.

그래야 나중에 위가 문제인지, 아래가 문제인지 정확하게 알 수 있기 때문이다.

그래서 assert는 런타임에 체크를 해주는 것이다.

실행을 했을 때 사용자가 지정 해준 범위 외에 행동을 하려고 한다고 하면 알려주는 것이다.

참고로 여기서 말씀드린 사용자는 다른 프로그래머를 말한다.

 


static assert

이번에는 아예 컴파일 타임에 오류를 발생시키는 static assert를 알려드리겠다.

 

static_assert라고 치고 뒤에 true나 false를 넣을 수 있다.

예를 들어

 

int x 는 5다

원래 assert를 써보면 x는 5여야 한다. assert(x == 5); 이게 가능하다.

(*논리연산자니까 등호 == 두 개 되는 거 주의)

왜냐하면 x는 변수니까 런타임에 결정이 돼도 assert는 가능하다.

 

그런데 static_assert는 컴파일 타임에 결정되는 경우에만 사용할 수 있다.

 

예를 들어 이 경우에는 x가 변수니까 컴파일 타임에 달라질 수 있다.

여기서 중간중간에 x는 10으로 바뀔 수도 있으니까 static_assert에서는 사용할 수없다.

 

 

const 상수일 때는 사용할 수 있다.


The difference between assert and static assert.

assert와 static_assert의 차이가 한 가지 더 있는데

static_assert는 뒤에 문구를 남길 수 있다.

 

이런 식으로 기록을 남길 수 있다.

x를 10으로 바꿔서 실행시켜보자.

 

그러면 바꾸는 순간 벌써 물결이 생긴다.

빌드해보면

 

x should be 5라면서 오류가 난다.

에러 메시지에 x should be 5 이게 출력이 된다.

 

여기까지 assert와 static_assert의 사용법과 컴파일러를 이용을 해서

내 프로그램이 문제가 생길 여지를 미리 차단하는 방법에 대해서 설명을 드렸다.

 

Comments