방송통신대학교 C++ 프로그래밍 교재에 있는 예제를 따라하다보니 종종 에러가 발생하는 것을 발견했다. 에러코드는 C4996, strcpy 구문에서 에러가 발생하는 것이었다. 컴파일러는 비쥬얼스튜디오 2022 컴파일러로 에러문구에는 strcpy_s 를 쓰라고 되어 있었는데 조금 이상하다는 생각을 했다. 왜냐하면 strcpy 가 표준라이브러리에 정의 되어 있었기 때문이고, 분명 이거 이런 저런 오픈 소스 코드를 훑어보았을 때많이 보았던 함수였기 때문이다. 일단 코드는 다음과 같다.
// StaticDM.cpp
#include <iostream>
#include "NamedObj.h"
using namespace std;
void f()
{
NamedObj x("Third");
// 세 번째 객체의 생성
x.display();
// 함수 반환 후 x는 소멸됨
}
int main()
{
cout << "NamedObj 클래스의 객체 수 : " << NamedObj::nObj() << endl;
NamedObj a("First"); // 첫 번째 객체 생성
NamedObj b("Second"); // 두 번째 객체 생성
a.display();
b.display();
f();
NamedObj c("Fourth"); // 네 번째 객체 생성
c.display();
cout << "NamedObj클래스의 객체 수 : " << NamedObj::nObj() << endl;
return 0;
}
// NamedObj.h
#ifndef NAMEDOBJ_H_INCLUDED
#define NAMEDOBJ_H_INCLUDED
#include <iostream>
using namespace std;
class NamedObj {
char* name;
int id;
//static 데이터 멤버
static int nConstr; // 생성된 객체 수
static int nDestr; // 소멸된 객체 수
public:
NamedObj(const char* s); // 생성자
~NamedObj(); // 소멸자
void display() const {
//객체 속성 출력
cout << "ID : " << id << "--> 이름 : " << name << endl;
}
static int nObj() {
// 존재하는 객체 수 반환
return nConstr - nDestr;
}
};
#endif
// NamedObj.cpp
#include <cstring>
#include "NamedObj.h"
NamedObj::NamedObj(const char* s)
{
name = new char[strlen(s) + 1];
// 문자열을 복사할 공간을 할당
strcpy(name, s);
id = ++nConstr;
// 생성된 객체의 수를 증가시키고 이를 ID로 부여
}
NamedObj::~NamedObj()
{
++nDestr;
// 소멸된 객체의 수를 증가시킴
delete[] name;
}
// static 데이터 멤버의 정의 및 초기화
int NamedObj::nConstr = 0;
int NamedObj::nDestr = 0;
일단 소스코드에 대한 설명을 좀 하자면 간단하게 생성된 객체 수와 소멸된 객체 수를 나타내는 코드이다. 설명을 위한 코드라 조금 이상하다고 생각할 수도 있겠다 내가 해석하기로는 StaticDM.cpp 의 f() 함수를 별도로 둠으로써 객체의 생성과 소멸을 나타낸 것 같다 그래서 1, 2, 4 번 객체도 main 함수가 종료 되면 소멸될 것이다.
strcpy 에 대해서도 조금 설명하는게 나을 것 같다. 해당 함수는 C 라이브러리에 선언 되어 있는 함수로 Cpp 에서는 cstring을 통해 전처리한다. 해당 함수는 널문자를 표현하여 string2 >> string1 으로 복사한다. 정리하자면 strcpy(destination, source) 로 정리할 수 있을 것이다.
(참고링크 : https://www.ibm.com/docs/ko/i/7.3?topic=functions-strcpy-copy-strings)
이제 C4996 에러가 발생하는 에러에 대해 알아보자, 해당 에러메시지는 다음과 같은 주석과 함께 출력된다.
This function or variable may be unsafe. Consider using safe-version instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
보통은 버퍼 오버플로우를 발생할 수 있기 때문에 에러를 띄운다. strcpy는 단순히 복사를 하므로 src 보다 dest의 크기가 작을 시 여러 문제를 발생할 수 있다. 그래서 strcpy를 쓸 때는 해당 값의 크기를 비교하여 미리 유효성 검사를 해야하지만, 일단 해당 코드는 예제코드이고, 충분히 원할히 동작함으로 예상되기 때문에 크게 신경쓰지 않아도 된다. 단지 이유만 알아두면 될 것이다.
그러면 strcpy_s 를 쓰는 것은 어떤 문제가 되나요? 라고 묻는다면 이건 gcc 에서 지원하지 않는다. 그 말은 즉슨 여러모로 호환성에서 떨어지는 코드라는 점에서 문제가 된다.
자 그러면 이제 strcpy 에 대한 에러를 비주얼 스튜디오에서 해제해보자.
방법은 여러가지가 있지만 나는 프로젝트 단위에서 에러코드를 해제하기로 했다.
프로젝트/구성속성/"C/C++"/전처리기 로 들어가서 왼쪽 상단에 모든 구성으로 되어 있는지 확인 후 전처리기 정의에 다음과 같은 구문을 추가하면 된다.
_CRT_SECURE_NO_WARNINGS
아니면 프로젝트/구성속성/"C/C++"/명령줄 에 들어가서 다음과 같은 추가 옵션을 추가해도 된다.
/wd4996
추가적으로 해당 문제를 해결하기 위해 gcc 에서는 strncpy를 지원한다.
해당 함수는 strncpy(destination, source, size_of_text) 형태로 문자열의 크기를 추가적으로 받기 때문에 안전하다. 해당 함수는 strncpy(dest, src, strlen(src) 와 같은 형태로 사용 하면 별도의 문자열 크기를 계산하지 않아도 된다.
예시로 제시된 코드처럼 헤더파일과 실제 선언된 함수들이 다른 경우에는 -o 옵션을 쓰면 된다. 해당 코드는 다음과 같은 명령어로 바이너리 파일을 만들 수 있다.
g++ StaticDM.cpp NamedObj.cpp -o main
정석은 -c 를 이용해 .o 인 오브젝트 파일을 만들어서 하는 거라고 알고는 있는데 다음과 같이 실행해도 충분히 동작한다. 또한 -o 옵션 뒤에 별도의 이름을 붙이지 않으면 기본 이름인 a.out 으로 생성된다.
'방송통신대학교 생활기 > Cpp' 카테고리의 다른 글
C++ 구조체, 클래스 차이 정리 (0) | 2022.09.14 |
---|