티스토리 뷰
memcpy() 함수와 다르게 overflow safe 이슈가 존재하지 않습니다. (memcpy_s() 함수는 안전합니다)
서론
memcpy() 함수는 복사 대상 버퍼 크기를 초과하여 복사를 진행할 수 있다.
이로인해 오버플로우 발생 위험이 있어 MS(Microsoft)에서 " memcpy_s() " 함수를 공개했다.
복사 대상 버퍼 크기와 복사할 데이터의 크기를 명시함으로써 함수의 안전성이 좋아졌다.
memcpy_s 함수에 대해 하나씩 살펴보자.
prototype
함수 prototype을 보면 MBCS, WBCS별로 사용하는 함수가 다르다.
하지만 memcpy_s() 함수 하나로도 WBCS 문자열 데이터도 복사할 수 있다. (자세한건 예제를 참고하자)
errno_t memcpy_s(
void *dest,
size_t destSize,
const void *src,
size_t count
);
errno_t wmemcpy_s(
wchar_t *dest,
size_t destSize,
const wchar_t *src,
size_t count
);
메모리 블록과 메모리 데이터
매개변수와 반환값
memcpy_s() 함수는 safe memory copy 의 줄임말로, 안전하게 버퍼 간에 메모리 데이터를 복사하는 함수이다.
memcpy_s() 함수를 호출할 때 필요한 매개변수와 반환 값은 다음과 같다.
매개변수 목록 | 설명 |
void* dest | destination 줄임말, 복사 대상 버퍼 |
size_t destSize | 복사 대상의 버퍼 크기 |
const void *src | source 줄임말, 복사할 값을 가지고 있는 버퍼 |
size_t count | 복사할 바이트 수 |
필요한 헤더 - ANSI: memory.h, string.h / UNICODE: tchar.h | |
> memcpy_s 함수는 destSize가 명시되어 있어 오버플로우 이슈가 발생하지 않습니다. |
source가 가르키는 곳에 count 크기의 메모리 데이터를 dest가 가리키는 곳에 복사하는 것이다.
(단, 대상 버퍼 크기(destSize)는 복사할 바이트 수(count)보다 작을 수 없음)
함수의 반환값은 메모리 복사가 성공적으로 이루어지면 0을 반환, 이외 errno_t 타입의 에러코드가 반환된다.
memcpy_s() 함수는 void* 타입을 매개변수로 전달할 수 있기 때문에 WBCS 지원 함수인 w_memcpy_s() 함수를 사용하지 않아도 된다.
그러므로 wmemcpy() 함수 관련 설명도 생략한다.
바이트 수만 잘 계산한다면 memcpy_s() 함수로도 WBCS 문자열 데이터를 복사할 수 있다.
dest에 복사된 값은 strcpy() 함수와 다르게 NULL-Terminated가 아니라는 것에 주의해야 한다.
만약 문자열을 메모리 복사하는 경우에 NULL값을 포함하여 복사하거나 복사한 문자열 맨 뒤에 NULL값을 대입시켜주도록 하자.
아쉽게도 동일한 영역의 메모리 복사는 불가능하다. 이럴 땐 memmove_s() 함수를 사용하도록 하자.
예제 및 그림을 통해 메모리 복사가 이루어지는 과정을 살펴보자
예제: wchar_t 문자열 멤버 변수 메모리 데이터 복사하기
다음은 stu1.name 변수의 메모리 데이터("Joy")를 stu.name[1] 주소에서 복사한 크기만큼 복사하는 예제이다.
#include "pch.h"
#include <iostream>
#include <tchar.h>
class student {
public:
wchar_t name[6];
int id;
void showData()
{
std::wcout << "name: " << name << ", " << "id: " << id << "\n";
}
};
int main()
{
student stu1 = { L"Joy", 40 };
std::cout << "stu1: ";
stu1.showData();
student stu2 = { L"James", 80 };
memcpy_s(&stu2.name[1], sizeof(stu2.name), stu1.name, (wcslen(stu1.name) * 2) + 2);
std::cout << "stu2: ";
stu2.showData();
return 0;
}
예제에서 사용되는 메모리 복사 호출 함수 코드이다.
이 코드가 작동됨으로서 기존 변수들이 가지고 있는 메모리 데이터의 변화를 그림으로 볼 것이다.
wcslen에서 *2를 하는 이유는 memcpy_s는 byte를 기준으로 하기 때문이다.
WBCS는 문자를 표현하는데 2byte를 사용된다.
마지막에 +2를 한 이유는 NULL 값을 포함시켜서 복사하기 위해서이다.
memcpy_s(&stu2.name[1], sizeof(stu2.name), stu1.name, (wcslen(stu1.name) * 2) + 2);
아래 그림에서 stu1 변수의 메모리 블럭 및 메모리 데이터를 볼 수 있다.
stu1.name 주소에서 8Byte만큼 복사 대상 위치에 복사하는 것이다.
중요한건, 복사할 메모리 블럭의 크기는 복사 대상 버퍼 크기를 초과할 수 없다.
중간 버퍼가 없으므로 복사 진행 중에 값이 바뀐다면 바뀌는 값이 복사될 수 있다.
마지막으로 메모리 복사가 이루어지면 stu2 메모리 블럭 및 메모리 데이터는 다음과 같이 변경될 것이다.
노란색으로 칠한 부분이 복사가 진행되어 메모리 데이터가 변경되었다고 볼 수 있다.
만약 "Joy" 가 아니라 "Johnson" 을 복사하려고 했다면 성공했을까 ?
> 당연히 실패할 수 밖에 없다, 복사 대상 버퍼 크기(12Byte)보다 복사할 메모리 블럭의 크기(14Byte)가 크기 때문이다.
메모리 복사가 진행된 후 stu2.name 변수 값을 출력하면 " JJoy "가 출력되는 것을 눈으로 볼 수 있다.
예제는 매우 간단한 코드로 구성되어 있으니 여러 가지 상황을 만들어서
직접 실습을 통해 연습하기 바란다.
메모리 복사는 어렵지 않고 재밌는 것이다.