방송통신대학교 생활기/Cpp

비주얼 스튜디오 C4996 에러

함나현 2022. 12. 7. 14:06

방송통신대학교 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 에러가 발생하는 에러에 대해 알아보자, 해당 에러메시지는 다음과 같은 주석과 함께 출력된다.

(참고 링크 : https://learn.microsoft.com/ko-kr/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4996?view=msvc-170&viewFallbackFrom=vs-2019)

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