컴파일러는 컴파일한 결과를 에러와 경고라는 진단 메시지로 출력한다.
모든 문법이 정확하다면 아무런 진단 메시지도 출력되지 않지만 사람이 컴퓨터가 아닌 한 보통은 한 두 개 정도의 메시지를 받게 된다.
이 중 에러는 명백하게 틀린 것이므로 반드시 수정한 후 재 컴파일해야 하나 경고는 경우에 따라 참고만 하고 무시해도 상관없다.
다음 예제는 별 특별한 동작은 하지 않지만 의도적으로 경고를 많이 받도록 작성해 본 것이다.
예 제 : Warning
#include <Turboc.h>
void main()
{
int i, j, k;
unsigned u, v = 1234;
double d = 3.14;
i = u;
if (i == 3)
{
i = d;
}
if (i == v)
{
switch (i)
{
}
}
}
컴파일하면 다음 여섯 개의 경고 메시지가 출력된다.
어디까지나 경고일 뿐이므로 컴파일은 일단 성공한다.
에러는 하나라도 있으면 실행 파일을 만들 수 없지만 경고만 있는 상태에서는 실행 파일을 만들 수 있다.
warning C4101: 'j' : unreferenced local variable
warning C4101: 'k' : unreferenced local variable
warning C4700: local variable 'u' used without having been initialized
warning C4244: '=' : conversion from 'double' to 'int', possible loss of data
warning C4018: '==' : signed/unsigned mismatch
warning C4060: switch statement contains no 'case' or 'default' labels
경고는 심각한 정도에 따라 1~4단계까지 레벨이 분류되어 있는데
비주얼 C++은 디폴트로 레벨 3까지의 경고를 출력하도록 되어 있다.
프로젝트 설정 대화상자의 C/C++ 페이지에서 경고 레벨을 4단계로 높이면 다음 추가 경고가 하나 더 발생한다.
warning C4706: assignment within conditional expression
각 경고의 의미는 영문으로 짧게 설명되어 있으며 C4101처럼 번호가 붙어 있는데 조금 더 구체적으로 설명해 보면 다음과 같다. MSDN의 인덱스창에서 C4101을 검색하면 경고가 발생한 원인에 대한 상세한 설명을 읽을 수 있다.
■ C4101 : 사용하지도 않은 지역변수를 선언했다는 뜻이다. 쓸데 없는 변수이므로 선언문을 삭제하는 것이 옳지만 컴파일하는데 지장은 없으므로 경고로 처리된다. 예제의 j, k 변수는 선언만 하고 실제 코드에서는 쓰지 않고 있다.
■ C4700 : 지역변수를 초기화하지 않고 사용했다는 뜻이다. 이 경우 쓰레기값을 그대로 사용되는데 일단 가능은 하지만 대부분의 경우 말썽을 일으킨다. 예제에서 i에 초기화하지 않은 u를 대입하는데 이때 u의 값이 무엇인지는 알 수 없으므로 i도 같이 쓰레기값이 가지게 될 것이다.
■ C4244 : i=d 대입문에 의해 i에 3이 대입되는데 이 과정에서 하강 변환이 발생해서 소수점 이하 0.14가 버려진다는 뜻이다. 만약 개발자가 실수값의 정수부만을 대입받고자 했다면 이것은 옳은 대입이지만 그렇지 않다면 일부 정보를 잃을 수 있으므로 경고로 처리한다.
■ C4018 : 부호있는 변수와 부호없는 변수를 상등 연산했으므로 좌우의 타입이 맞지 않다는 뜻이다. i와 v는 부호 여부가 다르므로 때로는 틀린 비교를 할 수도 있다.
■ C4060 : switch문의 case가 전혀 없어 이 switch 문 자체가 있으나 마나한 문장이라는 뜻이다. 일부러 이런 코드를 만들지는 않겠지만 개발 중에 case를 편집하다 보면 껍데기만 남는 경우가 가끔 있는데 이런 경고를 받았으면 빈 switch 문을 삭제하든가 아니면 case를 작성해야 한다.
■ C4706 : 조건문에 대입 연산자를 사용했다는 경고이다. C 문법은 조건문을 관계 연산문으로 제한하지 않으므로 대입문을 사용하는 것도 적법하다. 그러나 ==을 =로 잘못 쓰는 실수를 흔히 하기 때문에 경고로 이 사실을 알려 준다. 이 경고는 레벨 4로 일반적인 경고보다 수준이 낮다.
개발자는 이런 경고 메시지를 보고 자신의 코드가 잘못되었는지 점검해 보고 컴파일러의 경고대로 코드를 수정하거나 아니면 별 이상이 없을 경우 경고를 무시할 것이다. 컴파일러는 개발자의 실수나 또는 호환성 문제, 성능상의 문제 등을 지적하기 위해 경고 메시지로 충고를 하는 것이다. 그런데 때로는 이런 친절한 경고 메시지가 무척 귀찮을 때도 있다.
예를 들어 사용하지 않는 지역변수의 경우 있다고 해서 별다른 해를 끼치지 않으며 잠시 후 쓸 계획이라면 이런 경고가 별 도움이 되지도 않고 귀찮기만 할 뿐이다. 그냥 무시해 버릴 수도 있지만 이런 경고 때문에 정작 읽어야 할 중요한 에러 메시지를 놓칠 수도 있다. 이런 사소한 문제에 대해서는 컴파일러가 더 참견하지 말았으면 하는 생각이 들 때가 있다. 또한 반대로 어떤 경고는 아주 중요해서 경고가 아닌 에러로 분명히 알려 줬으면 하는 경우도 있을 것이다. 컴파일러가 경고를 출력하는 방법을 바꾸고 싶다면 다음 명령을 사용한다.
#pragma warning(경고제어문:경고번호)
경고 제어문의 종류는 다음과 같으며 제어문 다음에 : 과 함께 대상 경고의 번호를 적는다. 경고 번호는 공백으로 구분하여 여러 개를 나열할 수 있으면 경고 제어문도 콜론으로 구분하여 여러 개를 나열할 수 있다.
제어문 설명
once:번호 반복되는 경고를 한 번만 출력한다.
default:번호 원래 설정대로 되돌린다.
disable:번호 경고를 출력하지 않는다.
error:번호 경고를 에러로 처리한다.
레벨:번호 경고의 레벨(1~4)을 변경한다.
push[,n] 모든 경고의 레벨을 저장한다. n이 있을 경우 저장과 동시에 전역 경고 레벨을 n으로 변경한다.
pop 스택에 마지막으로 저장된 경고 레벨을 복원한다.
소스의 어느 위치에나 다음 명령을 삽입하면 이후부터 컴파일러가 경고를 통제하는 방법이 바뀐다.
#pragma warning (disable:4101) // 경고를 무시하도록 한다.
#pragma warning (once:4101) // 4101번 경고를 한 번만 출력한다.
#pragma warning (error:4700) // 경고 대신 에러를 출력한다.
#pragma warning (3:4706) // 4706번 경고를 레벨 3으로 올린다.
disable:4101은 미사용 지역변수에 대한 경고를 아예 무시하도록 한다. 지역변수를 쓰건 말건 간섭하지 말라는 얘기다. 이 명령을 사용하면 미사용 지역변수에 대한 경고는 더 이상 출력되지 않는다. once:4101은 이 경고를 딱 한 번만 출력하라는 뜻인데 비슷한 경고가 너무 많이 반복될 때는 이 명령으로 반복된 출력을 한 번으로 제한한다. 예제에 j, k 두 개의 미사용 지역변수가 있는데 j에 대해서만 경고하고 k는 경고하지 않는다.
error:4700은 이 경고를 아예 에러처럼 취급하라는 뜻이다. 지역변수를 초기화하지 않은 상태로 쓰레기값을 바로 쓸 경우 심각한 문제가 될 수 있으므로 이런 실수를 하면 에러로 지적해서 아예 빌드를 못하도록 하라는 것이다. 에러가 있는 상태로는 컴파일을 완료할 수 없는데 이렇게 하면 개발자는 반드시 명시적으로 초기화를 하게 될 것이며 쓰레기값으로 인한 문제를 방지할 수 있다.
3:4706은 조건문에 대입 연산자를 쓸 경우를 레벨 3으로 높이는데 if (i = 3)같은 실수는 비록 적합한 코드라 하더라도 흔하게 하는 실수이므로 이런 코드를 보면 반드시 알려 달라는 뜻이다. 4706번 경고의 레벨이 4이므로 비주얼 C++의 기본 설정으로는 이 경고가 출력되지 않는다. push와 pop 명령은 경고 레벨을 잠시만 변경하고 싶을 때 사용한다.
#pragma warning(push)
// 중간에 경고 레벨을 마음대로 바꾼다.
#pragma warning(pop)
특정 함수에 대해서만 경고 출력 방법을 바꾸고 싶고 그 외의 코드는 디폴트를 적용하고 싶을 때 이 두 명령을 사용한다. 변경하기 전의 상태를 push 하여 저장해 놓고 마음대로 옵션을 변경한 후 pop 명령으로 다시 복원하면 이 코드 바깥은 영향을 받지 않는다.
'Programming > C / C++' 카테고리의 다른 글
소수점 둘째자리이하 자르기 (0) | 2015.02.07 |
---|---|
비트 연산자들의 진리표 (0) | 2015.02.07 |
파라미터(parameter) & 아규먼트(argument) (0) | 2015.02.07 |
ASCII Table (0) | 2015.02.05 |
#pragma once (0) | 2015.02.05 |