출처 : 마이크로소프트

 

앞으로 몇 회에 걸쳐 C++의 중요한 기능들을 하나씩 살펴볼 생각이다. 실제로는 별로 쓰이지 않으면서 지식욕을 채우는 데만 도움이 되는 내용보다는 실무에 도움이 되고 일반적으로 받아들여지고 있는 내용을 중심으로 엮어갈 계획이다. 이번 주제는 필자가 평소에 전하고 싶어했던 ‘예외’로 결정했지만, 앞으로의 주제들은 최대한 독자 여러분의 의견을 반영하고 싶다.

이현창 | 포씨소프트에서 근무하는 필자는 화상 통신 및 e-러닝 관련 소프트웨어를 제작하고 있다. 2003년에는 3년간의 병역특례 근무를 마치고 아주대학교에서의 마지막 대학 생활을 멋지게 마무리하기 위해 열심히 준비중이다.

C++를 만든 Stroustrup은 그의 저서 ‘The Design and Evolution of C++’에서 예외는 반환 값을 대체하기 위한 것이 아니라 결함을 허용하는(fault-tolerance) 시스템을 만들 수 있도록 하기 위한 것이라고 말했다. 에러가 발생한 경우에도 올바르게 동작할 수 있는 프로그램을 만드는 것은 중요한 주제이며 예외는 그러한 일에 적당한 도구이다. 하지만 예외는 기존의 반환 값을 사용한 에러 처리를 훌륭하게 대체할 수 있는 도구이기도 하다.
우선 예외를 사용하지 않은 코드를 한 번 보자. 그리고는 이 코드에서 예외를 사용하도록 고쳐보자. 다이어트 광고에 나오는 두 장의 사진처럼 명백한 차이점을 볼 수 있도록 말이다. <리스트 1>에서는 반환 값을 사용해 에러 처리를 하고 있다. 이러한 방식은 몇 가지 문제점을 가지고 있는데, 먼저 예외를 사용하도록 고친 다음 <리스트 2>와 비교해 보는 것이 좋을 것 같다.
<리스트 2>를 보면 작업 코드는 try 블럭에 모였고 에러 처리 코드는 catch 블럭에 모였다. try 블럭을 보면 이 함수에서 하는 일이 무엇인지 쉽게 알아볼 수 있다. 반면에 <리스트 1>은 예외 처리 코드와 작업 코드가 섞여 있어 함수를 읽기 어렵게 만들고 있다. 그 다음으로 <리스트 2>에서는 여러 군데 중복되어 흩어져 있던 에러 처리 코드들이 하나로 통합됐다. 그렇기 때문에 Log()를 호출하는 대신 다른 일을 하고자 할 때도 수정 작업이 쉽게 이뤄질 것이다. 반면에 <리스트 1>에서는 여러 군데 흩어져 있는 에러 처리 코드를 일일이 수정해야 한다. 소프트웨어 개발에 있어 중복은 개발자가 언제나 피해야 할 것 중에 하나다.
작업 효율에 대해서도 한 번 생각해 보자. <리스트 1>처럼 무슨 일을 할 때마다 비교문을 둬 성공여부를 체크하는 것은 개발자에게 있어서 매우 피곤한 일이며 에러 처리 코드를 작성하는 데 많은 시간을 빼앗기도록 만든다. 예외를 올바르게 사용하면 개발자의 시간이 더 창조적인 일에 사용될 수 있을 것이다.
사실 <리스트 2>처럼 단순히 로그를 찍기 위해 예외를 잡는 것은 납득할 만한 설계가 아니다. 상위의 함수에서 예외 처리할 수 있는 경우에는 굳이 예외를 잡을 필요가 없다. 그런 경우에는 말 그대로 그냥 잡지 않으면 된다. 다음은 예외가 그냥 지나쳐 가도록 변경한 예제 코드다.

// sUserInfo 문자열을 분석해 USER_INFO 구조체를 채우는 함수
bool UserList::ParseUserInfo(MyString sUserInfo, USER_INFO& ui)
{
// ? 문자열 분석기를 구한다.
MyParser& parser = MyParser::GetParser(sUserInfo);

// ? 사용자 ID를 얻는다.
parser.Find ( “ID”, ui.userID );

// ? 사용자 이름을 얻는다.
parser.Find ( “Name”, ui.name );

// ? 사용자 나이를 얻는다.
parser.Find ( “Age”, ui.age );
}

<리스트 1>과 비교했을 때 명백한 차이점을 볼 수 있다. 보다 간결하고, 이해하기 쉽고, 유지 보수하기 쉬운 함수가 됐다. 앞의 코드는 기사에 맞게 작성된 것이긴 하지만 꾸며낸 상황은 아니다. 대부분의 함수에서는 예외를 던지거나 잡을 일이 없다(그리고 이것은 정말 중요한 특징이다). 에러가 발생한 곳에서는 예외를 던지면 되고, 예외가 필요한 곳에서만 예외를 받으면 된다.
언제 예외를 처리해야 하는지 잘 이해되지 않는다면 다음의 가이드라인이 도움이 될 것이다. 다음은 어떤 함수에서 예외를 처리해야 하는 지에 대한 간단한 가이드라인이다.

◆ 함수가 예외를 가지고 무엇을 해야 할 지 아는 경우
◆ 함수가 예외를 적당히 처리할 수 있고, 상위 함수가 예외를 가지고 무엇을 해야 할 지 모르는 경우
◆ 예외를 던지는 것이 프로그램을 비정상 종료시킬 가능성이 있는 경우
◆ 함수가 자신의 일을 계속 진행시킬 수 있는 경우
◆ 리소스를 해제할 필요가 있는 경우

결국은 논리적으로 생각해 알맞게 하는 것이 정답일 것이다. 프로그램 전체적인 관점에서 고려한다면 다음과 같은 가이드라인이 도움이 될 것이다.

◆ 보통의 저수준 함수에서는 예상할 수 있는 예외만을 처리한다.
◆ 그 밖의 예외는 고수준 함수에서 처리한다.
◆ 정말 중요한 저수준 함수에서는 예상할 수 없는 예외도 처리한다. 그리고 적당한 예외를 사용해 외부에 다시 던져준다.

앞서 저수준 함수란 엔진이나 라이브러리 같이 낮은 계층에 속하는 함수에 해당하고, 고수준 함수란 라이브러리를 사용하는 높은 계층의 함수에 해당한다.

예외에 안전하게 설계하기
다음은 예외로 인해 리소스의 유실이 발생할 수 있는 경우에 대한 예제이며 아주 흔한 것이다.

void Func()
{
// 객체의 생성
MyObj* obj = new MyObj();

// 메모리 블럭을 가지고 무엇인가 한다. 실패한 경우 예외를 발생시킨다.
MayThrowException(obj);

// 메모리 해제
delete obj;
}

만약 MayThrowException() 함수에서 예외가 발생하게 된다면 앞서 obj에 할당된 메모리가 해제될 기회를 놓치게 될 것이다. 실제로 프로그램 전반적으로 예외를 사용하고 있다면 이런 상황에 놓일 가능성이 크다. 이런 상황에 대한 일반적인 해결책은 스택을 이용하는 것이다. 다음은 리소스 유실을 막기 위해 클래스를 도입한 예제 코드다.

// 리소스 유실을 막기 위한 클래스
class AvoidLeak
{
public:
AvoidLeak(MyObj* p) : _p(p) {} // 포인터 보관
~AvoidLeak() {delete _p;} // 보관된 포인터 해제
private:
MyObj* _p;
};

void Func()
{
// 객체의 생성
MyObj* obj = new MyObj();

// 리소스 유실 방지용 객체 생성
AvoidLeak al(obj);

// 메모리 블럭을 가지고 무엇인가 한다. 실패한 경우 예외를 발생시킨다.
MayThrowException(obj);

// 메모리를 수동으로 해제할 필요가 없다
}
AvoidLeak 클래스의 소멸자에서는 보관된 포인터를 해제한다. 함수가 정상적으로 종료하는 경우나 예외가 발생해 종료하는 경우 모두 지역 변수의 소멸자는 반드시 호출되도록 정해져 있다. 그렇기 때문에 MayThrowException() 함수에서 예외가 발생하는 경우라도 AvoidLeak 클래스의 소멸자는 올바르게 호출되고 메모리 블럭도 올바르게 해제된다.
예외를 사용해 에러 처리를 한다면 예외에 안전하도록 설계하는 데 신경써야 한다. 간단하게는 <리스트 3>처럼 예외가 발생하는 경우라도 리소스 유실이 일어나지 않도록 설계해야 한다. 더 나아가 예외가 발생하는 경우라도 객체가 올바른 상태를 유지할 수 있도록 설계하는 것이 중요하다. 예를 들어 A라는 작업이 실제로는 a1 - a2 - a3라는 작업으로 이뤄진다고 가정해 보자. 작업 a2가 수행되는 과정에서 에러가 발생했다면 a1의 작업은 올바르게 복구(rollback)될 수 있도록 설계돼야 한다.
<리스트 3>은 내부적으로 필요한 리소스를 초기화하는 함수다. 이 함수에서는 예외가 발생할 수 있는 곳이 여러 군데 있는데, new 연산자에서 메모리 할당을 실패하는 경우에도 예외가 발생할 수 있고 Commit() 내에서 실패하는 경우에도 예외가 발생할 수 있다.
만약에 이 함수가 실행되고 리소스 ?번을 초기화하는 과정에서 예외가 발생한다면 어떻게 될까? LargeObject 객체는 일부만 초기화되어 있는 올바르지 않은 상태가 될 것이고 이는 여러 경우에 문제를 발생시킬 것이다. <리스트 4>는 이러한 문제에 대한 일반적인 해결책이다.
auto_ptr은 <리스트 4>에 나오는 AvoidLeak과 같은 클래스로서 예외가 발생하는 경우에도 리소스가 올바르게 해제될 수 있도록 하는 역할을 한다(auto_ptr에 대한 자세한 설명은 박스 기사에서 볼 수 있다). 그리고 MyResource를 LargeObject 객체의 멤버 변수에 보관하는 대신 지역 변수에 보관했기 때문에 예외가 발생하는 경우라도 LargeObject 객체의 내부 상태는 올바르게 유지된다. 모든 작업이 성공적으로 완료된 후에야 LargeObject 객체의 내부 상태가 변경된다. 이러한 패턴을 다음과 같이 요약해 볼 수 있다.

짾 예외가 발생할 만한 작업은 지역 변수를 사용해 진행한다.
짿 안전한 방법만을 사용해 클래스의 내부 상태를 변경한다.

예외에 관한 이런 저런 이야기
생성자에서 예외 발생
모두가 알고 있듯 생성자는 반환 값을 가지고 있지 않다. 그렇기 때문에 생성자에서 발생한 에러를 알리기 위해 예외를 사용하는 것은 아주 좋은 생각이다. 그러나 그것이 의도적이든 의도적이지 않든 간에 생성자에서 예외가 발생하는 경우에는 다음과 같은 몇 가지 규칙을 알아둬야 한다.

짾 생성자에서 예외가 발생한 경우 객체는 생성되지 않는다 : 객체의 생존 기간은 생성자의 끝(닫는 괄호)에서부터 소멸자의 시작(여는 괄호)까지이다. 생성자에서 예외가 발생한 경우에는 생성자의 끝에 도달하지 못한 것이고 결국 생성되지 못한 것이 된다. 그렇기 때문에 객체의 소멸자는 호출되지 않는다. 소멸자가 호출되지 않기 때문에 생성자에서 할당한 리소스가 올바르게 제거되지 않을 수 있다는 점을 기억해야 한다. 생성자에서 예외가 발생할 가능성이 있는 경우에는 catch문을 두어 리소스를 깨끗이 정리하고 다시 예외를 던져야 한다.

짿 생성자에서 예외가 발생한 경우 이미 생성된 멤버들은 어떻게 처리될까 : 생성자에서 예외가 발생한 경우 어떤 멤버는 이미 생성되었고, 어떤 멤버는 아직 생성되지 않았을 수 있다. 예를 들어 이미 생성된 멤버가 메모리를 할당받았다면 이 메모리는 어떻게 해제할 수 있을까? 다행히도 우리가 신경써줘야 하는 부분은 없다. 이미 생성된 멤버의 경우에는 올바르게 소멸자가 호출되는 것을 확인해 볼 수 있다(<리스트 5>).

Member 클래스는 두 번째 멤버 객체를 생성할 때 생성자에서 예외를 던지도록 제작된 클래스다. 그렇기 때문에 Container 클래스의 생성자에서 두 번째 멤버 객체인 m2를 생성할 때 예외가 발생될 것이다. <리스트 5>의 결과에서 볼 수 있듯 완전하게 생성된 m1은 올바르게 소멸자가 호출됐다. m2는 자신의 생성자에서 예외가 발생했기 때문에 생성되지 못했다. m3는 생성자조차 호출되지 못했다.
new[] 연산자를 사용해 객체의 배열을 생성하는 경우에도 같은 원칙이 적용된다. 객체의 배열을 생성하는 도중에 예외가 발생한다면 이미 생성된 객체가 있을 수 있고, 아직 생성되지 않은 객체가 있을 수 있다. 이 경우에도 이미 생성된 객체에 대해서는 소멸자가 올바르게 호출되는 것을 확인할 수 있다.

쨁 생성자의 초기화 리스트에서 발생하는 예외는 어떻게 잡을까 : 생성자의 초기화 리스트에서는 부모 클래스의 생성자나 멤버 생성자를 직접 호출할 수 있으며 생성자의 본문 밖에 위치한다. 초기화 리스트에서 예외가 발생하는 경우에는 어떻게 잡을 수 있을까? <리스트 6>은 생성자의 초기화 리스트에서 발생하는 예외를 잡는 방법을 보여준다.

필자에게도 그다지 익숙하지 않은 문법이지만 초기화 리스트에서 발생하는 예외를 잡을 필요가 있을 때는 유용하게 사용할 수 있을 것이다. 한 가지 아쉬운 것은 비주얼 C++ 6.0에서는 이와 같은 문법을 지원하지 않는다는 점이다. 그러나 다음 버전인 비주얼 C++.NET에서는 사용 가능하다.

소멸자에서 예외 발생
스택을 정리하는 과정에서 예외가 발생하면 응용 프로그램이 비정상적으로 종료될 수 있기 때문에 소멸자에서는 절대로 예외가 발생해서는 안 된다. 소멸자에서 예외를 발생시킬 수 있는 함수를 호출하는 경우에는 반드시 catch로 잡아주도록 하자. 또한 delete나 delete[]를 오버로드한 경우 역시 절대로 예외가 발생하지 않도록 주의해 제작해야 한다.

Win32 구조적 예외 처리
Win32 구조적 예외 처리(Structured Exception Handling, 이하 SEH)는 윈도우 운영체제 수준에서 구현되어 있는 예외 처리 기능이다. 여기서의 예외는 C++의 예외와는 다른 것이며, 잘못된 메모리 참조나 스택 오버플로우와 같은 시스템 수준의 예외를 말한다. 우선 SEH를 사용해 잘못된 메모리 참조 예외를 잡는 방법을 살펴보자.

void Func()
{
__try
{
// 잘못된 주소 참조 오류를 발생시킨다.
char* p = NULL;
*p = ‘A’;
}
__except ( EXCEPTION_ACCESS_VIOLATION == GetExceptionCode()
? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH )
{
// Access violation
}
}

SEH는 C++의 try, catch와는 달리 __try, __except 키워드를 사용한다. GetExceptionCode()는 예외의 종류를 알려주는 함수이고, 이 예제에서는 잘못된 메모리 참조인지 비교하고 있다. 잘못된 메모리 참조인 경우에는 EXCEPTION_EXCUTE_HANDLER 값을 반환함으로써 __except 블럭이 실행되도록 하고, 잘못된 메모리 참조가 아닌 경우에는 EXCEPTION_CONTINUE_SEARCH 값을 반환해 다른 __except 블럭을 찾아보도록 하고 있다.
SEH는 시스템에서 발생하는 예외를 잡을 수 있을 만큼 강력하지만, C++ 객체가 선언된 함수에서는 함께 사용할 수 없다(좀더 자세히 말해 함수 종료시 호출돼야 할 소멸자가 있는 함수를 말한다). 다행히도 SEH 예외를 C++ 예외처럼 다룰 수 있는 방법이 있는데, 이 방법을 사용하면 같은 함수 안에서도 SEH와 C++ 예외를 함께 사용할 수 있을 뿐더러 일관된 예외 처리 방식을 사용할 수 있어서 좋다. 다음은 SEH 예외를 C++ 예외로 만든 예제다.

#include “stdafx.h”
#include
#include

// SEH 예외는 이 예외 클래스로 던져진다.
class CSEHException
{
public:
CSEHException(UINT excode, PEXCEPTION_POINTERS exp)
{
// SEH 예외와 관련된 코드를 저장한다.
m_exceptionCode = excode;
m_exceptionPointer = *exp;
}

int GetExceptionCode() {return m_exceptionCode;}

UINT m_exceptionCode; // 예외 코드
EXCEPTION_POINTERS m_exceptionPointer; // 부가 정보
};

// SEH를 C++ 예외로 변경하기 위한 함수(시스템에 의해 호출된다)
void _cdecl SEH2CppException(UINT excode, PEXCEPTION_POINTERS exp)
{
throw CSEHException(excode, exp);
}

void main()
{
// SEH 변경 함수를 등록한다.
_set_se_translator(&SEH2CppException);

try
{
// 잘못된 주소 참조 예외를 발생시킨다.
int* i = NULL;
*i = 123;
}
catch( CSEHException& e)
{
switch(e.GetExceptionCode() )
{
case EXCEPTION_ACCESS_VIOLATION:
// 여기가 실행된다.
break;
}
}
}

SEH 예외를 C++ 예외로 만드는 방법은 아주 간단하다. 시스템은 SEH 예외가 발생할 때마다 정해진 함수를 호출하도록 되어 있는데, _set_se_translator()를 사용하면 우리가 작성한 함수를 호출하도록 만들 수 있다. 그리고 우리가 작성한 함수 안에서는 C++ 예외를 던지면 된다.
앞 예제에서는 SEH 에외가 발생한 경우 SEH2CppException()을 호출하도록 지정했으며, SEH2CppException() 안에서는 CSEHException 객체를 예외로 던지는 것을 확인할 수 있다. 또한, main() 함수를 보면 잘못된 메모리 참조 예외가 CSEHException로 던져지는 것을 확인할 수 있다. CSEHException을 사용하는 것이 얼마나 편리한지 보여주기 위해 이 기능을 사용하지 않은 경우와 사용하는 경우를 비교해 보았다(<리스트 7, 8>).
누가 보아도 <리스트 8>의 경우가 훨씬 이해하기 쉽다. 게다가 더욱 안전하다. 포인터가 NULL이 아닌 경우라도 얼마든지 잘못된 주소를 가리키고 있을 수 있다는 사실을 명심하자! 이런 경우에 <리스트 7>은 심각한 문제를 일으키게 되지만 <리스트 8>은 안전하게 예외를 잡아낼 수 있다. 그렇다고 해서 <리스트 8>이 성능 상의 문제를 가지고 있는 것은 아니다. 예외가 빈번히 일어나는 것이 아니라는 가정에서 <리스트 8>은 비슷하거나 더 빠르게 수행된다.

기사에 대한 많은 조언을 당부하며
이번 호에는 많은 전문가들을 통해 밝혀지고 알려진 올바른 예외 사용법에 대해 알아봤다. 안타깝게도 지면이 부족한 관계로 몇 가지 중요한 정보를 전달하지 못했는데, 다음과 같은 것들이다. 이런 부분은 참고 서적을 통해 충분한 지식을 습득할 수 있을 것이라고 생각한다.

◆ new 연산자에서 할당에 실패한 경우의 올바른 처리 방법
◆ 프로젝트에 사용할 기반 예외 클래스를 작성하는 방법
◆ 예외에 안전한 클래스를 작성하는 방법에 대한 보다 깊은 지식

이번 기사를 작성하면서 가장 어려운 부분은 어떤 내용이 실무 개발자들에게 도움이 될지에 대한 정보가 부족했다는 점이다. 이번 기사에 대한 질책이나 다음 호에 대한 조언, 희망 사항을 들을 수 있다면 좀더 좋은 기사를 쓰는 데 도움이 될 것이다.


[ auto_ptr 클래스 ]
AvoidLeak은 MyObj*에 대해서만 사용할 수 있지만, 템플릿을 사용하면 쉽게 여러 가지 타입을 담을 수 있는 클래스로 만들 수 있다. 실제로 C++ 표준 라이브러리에는 auto_ptr 클래스가 이러한 용도로 제공된다.
하지만 auto_ptr은 배열에 대해 사용할 수 없으며, 소유권 이전 방식의 복사 기능을 제공하기 때문에 STL 컨테이너에 보관할 수 없다. http://boost.org에서는 이러한 문제점을 해결한 scoped_array이나 shared_ptr과 같은 클래스를 제공한다. 자세한 정보는 해당 사이트에서 얻을 수 있다.

[ 컴파일 옵션의 사용 ]
<리스트 8>을 컴파일하기 위해서는 컴파일 옵션으로 /EHa를 주어야 한다. 그렇지 않은 경우 비주얼 C++.NET에서는 다음과 같은 경고 메시지를 출력한다.
warning C4535: calling _set_se_translator() requires /EHa
the command line options /EHc and /GX are insufficient

다음은 예외와 관련된 컴파일 옵션이다.
/EHa 비동기 모드
/EHs 동기 모드
/GX (/EHsc) 동기 모드. 끝에 붙은 c는 extern “C” 함수는 예외를 발생시키지 않음을 의미

비동기 모드는 함수 또는 함수에서 호출하는 자식 함수에 throw문이 없어도 예외가 발생할 수 있다고 가정한다. SEH 예외는 throw문이 없어도 언제든지 발생할 수 있기 때문에 비동기 모드로 컴파일해야 한다.

블로그 이미지

요다할아범

,

사례1)

warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss


닷넷에서 한글로 주석을 달면 나올 수 있는 워닝이라고 한다...

그래서인지 구글에서 검색해보면 이 워닝으로 귀찮아하는 외국인을 찾을 수 없다ㅎㅎ


#pragma warning(disable:4819) 로 무시해주거나~

주석을 영어로 달면 해결될듯?? <- 이방법은 안해봤으나 앞으로는 이렇게 해야겠다고 맘먹는중??ㅎㅎ


사례2)


warning C4819: 현재 코드 페이지(949)에서 표시할 수 없는 문자가 파일에 들어 있습니다. 데이터가 손실되지 않게 하려면 해당 파일을 유니코드 형식으로 저장하십시오


d3d9types.h 에 보면..


1801번째 줄에서 볼수 있는 구조체 이다.

( Ctrl + G 를 누르면 한방에 갈 수 있다는 것은 알고 있죠?! 마우스 휠 비비지 말자.. 남들 보면 오해한다.. -0-? 뭘?! )


typedef struct _D3DDEVINFO_VCACHE {
    DWORD   Pattern;                    /* bit pattern, return value must be FOUR_CC(멌? 멇? 멌? 멖? */
    DWORD   OptMethod;                  /* optimization method 0 means longest strips, 1 means vertex cache based */
    DWORD   CacheSize;                  /* cache size to optimize for  (only required if type is 1) */
    DWORD   MagicNumber;                /* used to determine when to restart strips (only required if type is 1)*/
} D3DDEVINFO_VCACHE, *LPD3DDEVINFO_VCACHE;


주석문중에 깨진 글자가 보일 것이다.


과감히 지워주자... 이제 더이상의 찜찜한 기분은 느낄 필요가 없다..



VS.NET 2003 혹은 2005로 컴파일을 하다보면


warning C4819: 현재 코드 페이지(949)에서 표시할 수 없는 문자가 파일에 들어 있습니다. 데이터가 손실되지 않게 하려면 해당 파일을 유니코드 형식으로 저장하십시오.


이런 형태의 warning 메세지를 확인할 수 있습니다. warning 이기는 하지만 그래서 상당히 눈에 거슬리는 부분이 아닐 수 없습니다.

그래서 이번에 그 원인과 해결방법을 알아 보고자 합니다.



[원인]

ANSI로 인코딩된 소스코드를 VS.NET 2005 (한글판)에서 빌드하면서 발생하는 문제로 일본판의 경우에도 비슷한 문제가 발생하였고 영문판 VS.NET 2005에서는 발생하지 않는 것으로 알려졌습니다.

MS에서도 이를 버그로 인정하고 다음 버전에서 수정한다고 합니다.



[해결방안1]

#pragma warning(disable: 4819)

이 문장을 상단에 위치시켜서 해당 warning이 표시되지 않게 한다.



[해결방안2]

VS.NET 2005의 경우

'프로젝트->속성->구성속성->C/C++->고급->특정 경고 사용 안함'

항목에 4819와 같이 무시하고 싶은 경고 메세지 번호를 넣는다.



[해결방안3]

세번째로는 warning이 발생하는 파일을 새롭게 유니코드 형태로 저장하는 방법입니다.


VS.NET 2005의 경우

'파일->저장 고급 옵션'

"유니코드 - 코드페이지 1200" 형태로 저장을 선택하시면 됩니다.


메모장 등 기타 편집기를 이용하셔도 됩니다.



현재 전반적으로 사용하는 방법이 위에 3가지 정도 인듯 합니다.

저의 경우 이 warning이 DXSDK에서 발생해서 당황했었던... ^^

처음엔 #pragma 형태로 했었는데 해결방안2 형태로 하니까 편하더군요.. ^^

블로그 이미지

요다할아범

,

비쥬얼 스튜디오 2005에서 컴파일 하다 보면 위의 warning이 뜨는것을 볼 수 있다.

몇가지 경우가 있는듯 한데

대표적으로 예전의 문자열 관련 CRT 함수를 쓰면 나타난다.

위 warning의 의미는 "VS2005에서 이전 문자열 관련 CRT함수의 안전성을 강화하기 위하여

"_s"가 붙는 함수를 다시 만들었기 때문에 될 수 있으면 이전에 사용하던 함수는 사용하지 마라" 정도가 되겠다.


예) strcpy  -> strcpy_s


안전성이라고 해봐야 특별한것은 없고 메모리 관련 에러가 나면 확실하게 에러를 표시해 주는 기능 정도인거 같다 (이전 함수들은 메모리 버퍼의 크기에 대한 보장을 못함). 그래도 메모리 관련 에러가 나면 그 결과가 엉뚱한 데서 나타날 수가 있으므로 여간 찾기 어려운 것이 아닌데 이런 사소한것 부터 조심스럽게 쓰면 좀 더 안전하게 코딩할 수 있지 않을까 생각이 든다.

그래서 요즘 드는 생각들이 C 함수 보다는 C++이 제공해주는 (엄밀하게 말한다면

C++ 로 만든 표준 라이브러리)클래스를 보다 폭넓게 사용하는것이 좋겠다라는 생각이 든다.

STL에서 제공해주는 스트링 클래스나 여러 컨테이너를 사용한다면 직접 문자열을 다룬다거나

new delete를 사용하는 빈도가 줄어들터이니...


java가 부러운점이 있다면

필요로하는 거의모든 라이브러리를 표준차원에서 제공해준다는것이다.

C++ 같은경우 간단한 문법(사실 문법자체도 어렵거니와)과 STL을 뺀다면

표준이랄께 없기 때문이다.

투정이 좀 길었는데

처음으로 돌아가서

만약 이전에 짜놓았던 코드를 고치기 힘들면 복잡하게 생각할꺼 없고

stdafx.h 파일의 최 상단에

#define _CRT_SECURE_NO_DEPRECATE

한줄 넣어버리면 그만이다...

블로그 이미지

요다할아범

,